vmm-0.6.1/0000755000175000017500000000000012033032424010500 5ustar pvopvovmm-0.6.1/COPYING0000644000175000017500000000274512033032424011543 0ustar pvopvoCopyright (c) 2007 - 2012, Pascal Volk All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the names of the authors nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. vmm-0.6.1/setup.py0000644000175000017500000000477412033032424012226 0ustar pvopvo#!/usr/bin/env python # -*- coding: utf-8 -*- # Copyright 2007 - 2012, Pascal Volk # See COPYING for distribution information. import os from distutils.core import setup from distutils.dist import DistributionMetadata VERSION = '0.6.1' descr = 'Tool to manage mail domains/accounts/aliases for Dovecot and Postfix' long_description = """ vmm, a virtual mail manager, is a command line tool for administrators/postmasters to manage (alias-)domains, accounts, aliases and relocated users. It is designed for Dovecot and Postfix with a PostgreSQL backend. """ packages = [ 'VirtualMailManager', 'VirtualMailManager.cli', 'VirtualMailManager.ext', 'VirtualMailManager.pycompat', ] # http://pypi.python.org/pypi?%3Aaction=list_classifiers classifiers = ['Development Status :: 5 - Production/Stable', 'Environment :: Console', 'Intended Audience :: System Administrators', 'License :: OSI Approved :: BSD License', 'Natural Language :: Dutch', 'Natural Language :: English', 'Natural Language :: Finnish', 'Natural Language :: French', 'Natural Language :: German', 'Natural Language :: Vietnamese', 'Operating System :: POSIX', 'Operating System :: POSIX :: BSD', 'Operating System :: POSIX :: Linux', 'Operating System :: POSIX :: Other', 'Programming Language :: Python', 'Programming Language :: Python :: 2', 'Topic :: Communications :: Email', 'Topic :: System :: Systems Administration', 'Topic :: Utilities'] # sucessfuly tested on: platforms = ['freebsd7', 'linux2', 'openbsd4'] # remove existing MANIFEST if os.path.exists('MANIFEST'): os.remove('MANIFEST') setup_args = {'name': 'VirtualMailManager', 'version': VERSION, 'description': descr, 'long_description': long_description, 'packages': packages, 'author': 'Pascal Volk', 'author_email': 'user+vmm@localhost.localdomain.org', 'license': 'BSD License', 'url': 'http://vmm.localdomain.org/', 'download_url':'http://sf.net/projects/vmm/files/', 'platforms': platforms, 'classifiers': classifiers} if 'requires' in DistributionMetadata._METHOD_BASENAMES: setup_args['requires'] = ['psycopg2 (>=2.0)', 'pyPgSQL (>=2.5.1)'] setup(**setup_args) vmm-0.6.1/vmm.cfg0000644000175000017500000001200212033032424011753 0ustar pvopvo# This is the vmm (a virtual mail manager) configuration file. # default location: /usr/local/etc/vmm.cfg # # # Database settings # [database] ; The Python PostgreSQL database adapter module to be used (String) ; Supported modules are: ; * psycopg2 ; * pyPgSQL module = psycopg2 ; Hostname or IP address of the database server (String) host = localhost ; The TCP port, on which the database server is listening for connections (Int) port = 5432 ; SSL mode for the database connection (String) ; Possible values are: ; * disabled ; * allow ; * prefer (default) ; * require ; * verify-ca (PostgreSQL >= 8.4) ; * verify-full (PostgreSQL >= 8.4) sslmode = prefer ; Database user name (String) user = vmm ; Database password (String) pass = dbpassword ; Database name (String) name = mailsys # # mailbox settings # [mailbox] ; The mailbox format to be used for user's mailboxes. (String) ; Depending on the used Dovecot version there are up to 3 supported formats: ; * maildir - since Dovecot v1.0.0 ; * mdbox - since Dovecot v2.0.beta5 ; * sdbox - since Dovecot v2.0.rc3 format = maildir ; A colon separated list of mailbox names, that should be created (String) ; e.g.: folders = Drafts:Sent:Templates:Trash:Lists.Dovecot:Lists.Postfix folders = Drafts:Sent:Templates:Trash ; Name of the mailbox root directory in a user's home. (String) ; Usually used names (format: name): ; * maildir: Maildir ; * mdbox: mdbox ; * sdbox: sdbox root = Maildir ; Set to true if the mailboxes from the folders option should be listed in ; the subscriptions file. (Boolean) subscribe = true # # Domain settings # [domain] ; Should vmm create the postmaster account when a new domain is created? ; (Boolean) auto_postmaster = true ; Delete domain directory recursive when deleting a domain? (Boolean) delete_directory = false ; Permissions for domain directories (Int) ; octal 0770 -> decimal 504 directory_mode = 504 ; Force deletion of accounts and aliases when deleting a domain (Boolean) force_deletion = false ; ; The service settings will be evaluated and applied when a domain is ; created. The service settings of the domain will be applied when you ; create a new account. ; Use the subcommand domainservices to modify a domain's service settings. ; Or userservices in order to update the service setting of an account. ; Allow smtp by default? (Boolean) smtp = true ; Allow pop3 by default? (Boolean) pop3 = true ; Allow imap by default? (Boolean) imap = true ; Allow managesieve by default? (Boolean) sieve = true ; ; The quota_* settings will be evaluated and applied when a domain is ; created. The domain's quota_* settings will be applied when an account ; is added to a domain. ; Use the subcommand domainquota to modify a domain's quota limits. ; Or userquota in order to update an account's quota limits. ; Quota limit in bytes. 0 means unlimited (String) ; The value can have one of the suffixes: ; * b: bytes ; * k: kilobytes ; * M: megabytes ; * G: gigabytes ; 1024 is the same as 1024b or 1k quota_bytes = 0 ; Quota limit in number of messages. 0 means unlimited (Int) quota_messages = 0 ; ; The transport setting will be evaluated and applied when a domain is ; created. The domain's transport setting will be applied when an account ; is added to a domain. ; Use the subcommand domaintransport to modify the transport of a domain. ; Or usertransport in order to update an account's transport setting. ; ; With Dovecot >= v2.0.0 it's strongly recommended that you use Dovecot's ; lmtp instead of the dovecot-lda. ;transport = lmtp:unix:private/dovecot-lmtp ; default transport for domains and accounts (String) transport = dovecot: # # Account settings # [account] ; Delete the user's home directory recursive when deleting an account? (Boolean) delete_directory = false ; Permissions for the user's home directory and mail directories (Int) ; octal 0700 -> decimal 448 directory_mode = 448 ; Display disk usage in account info by default? (Boolean) disk_usage = false ; Should vmm generate a random password when no password was given for the ; useradd subcommand? (Boolean) random_password = false ; How many characters to include in the generated passwords? (Int) password_length = 8 # # external binaries # [bin] ; location of dovecotpw (Dovecot v1) or doveadm (Dovecot v2) (String) dovecotpw = /usr/sbin/dovecotpw ; location of disk usage (String) du = /usr/bin/du ; location of postconf (String) postconf = /usr/sbin/postconf # # misc settings # [misc] ; The base directory for all domains/accounts (String) base_directory = /srv/mail ; Number of encryption rounds for the password_scheme BLF-CRYPT (Int) crypt_blowfish_rounds = 5 ; Number of encryption rounds for the password_scheme SHA256-CRYPT (Int) crypt_sha256_rounds = 5000 ; Number of encryption rounds for the password_scheme SHA512-CRYPT (Int) crypt_sha512_rounds = 5000 ; the version number from `dovecot --version` (String) ; e.g. 1.2.17, 2.0.21, 2.1.9 or 2.2.beta1 dovecot_version = 2.1.9 ; Password scheme to use (see also: ´vmm listpwschemes`) (String) password_scheme = CRAM-MD5 vmm-0.6.1/.hg_archival.txt0000644000175000017500000000023012033032424013561 0ustar pvopvorepo: bb0aa2102206cd25a99e64d0b94bcd6c2018d3df node: 6463832d690e20689c120e2858ba70d7f727e6a3 branch: default latesttag: vmm-0.6.1 latesttagdistance: 1 vmm-0.6.1/INSTALL0000644000175000017500000001724612033032424011543 0ustar pvopvoInstallation Prerequisites You should already have installed and configured Postfix, Dovecot and PostgreSQL. The Virtual Mail Manager depends on: - Python (>= 2.4.0) - Psycopg 2¹ or pyPgSQL² If you are using Python <= 2.5.0: - if you want to store your users' passwords as PLAIN-MD4 digest in the database, vmm will try to use Crypto.Hash.MD4 from PyCrypto³. - if you are using Dovecot >= v1.1.0 and you want to store your users' passwords as SHA256 or SSHA256 hashes, vmm will try to use Crypto.Hash.SHA256 from PyCrypto². For SHA256/SSHA256 you should have at least use PyCrypto in version 2.1.0alpha1. When the Crypto.Hash module couldn't be imported, vmm will use dovecotpw/doveadm, if the misc.password_scheme setting in the vmm.cfg is set to PLAIN-MD4, SHA256 or SSHA256 [1] Psycopg: (Debian: python-psycopg2) [2] pyPgSQL: (Debian: python-pgsql) [3] PyCrypto: (Debian: python-crypto) Create additionally a user and groups for improved security We will create the system user `doveauth'. This user is used in the authentication process. On a Debian GNU/Linux System use this command: adduser --system --home /nonexistent --no-create-home --group \ --disabled-login --gecos "Dovecot IMAP/POP3 authentication user" \ doveauth This will create the doveauth user and group. For Dovecot >= 2.0 we create also the group `dovemail'. Dovecot will assign this group to all Dovecot processes. On a Debian GNU/Linux bases system run: addgroup --system dovemail Configuring PostgreSQL (for more details see: http://vmm.localdomain.org/installation/postgresql_configuration.html) * /etc/postgresql/8.4/main/pg_hba.conf [ if you prefer to connect via TCP/IP ] # IPv4 local connections: host mailsys +mailsys 127.0.0.1/32 md5 [ if you want to connect through a local Unix-domain socket ] # "local" is for Unix domain socket connections only local mailsys +mailsys md5 # reload configuration /etc/init.d/postgresql-8.4 force-reload * Create a database superuser if necessary: # as root run: su - postgres # if you have sudo privileges run: sudo su - postgres # create your superuser, which will be able to create users and databases createuser -s -d -r -E -e -P $USERNAME * As superuser create the database and db users for vmm, Postfix and Dovecot connecting to PostgreSQL: psql template1 # create users, group and the database CREATE ROLE vmm LOGIN ENCRYPTED PASSWORD 'DB PASSWORD for vmm'; CREATE ROLE dovecot LOGIN ENCRYPTED password 'DB PASSWORD for Dovecot'; CREATE ROLE postfix LOGIN ENCRYPTED password 'DB PASSWORD for Postfix'; CREATE ROLE mailsys WITH USER postfix, dovecot, vmm; CREATE DATABASE mailsys WITH OWNER vmm ENCODING 'UTF8'; \q # connect to the new database psql mailsys vmm -W -h 127.0.0.1 # either import the database structure for Dovecot v1.0.x/v1.1.x \i vmm-y.x.z/pgsql/create_tables.pgsql # or import the database structure for Dovecot v1.2.x/v2.x \i vmm-x.y.z/pgsql/create_tables-dovecot-1.2.x.pgsql # leave psql \q # set permissions for your Dovecot and Postfix users # see python set-permissions.py -h for details python vmm-x.y.z/pgsql/set-permissions.py -a -H 127.0.0.1 -U vmm Create directory for your mails mkdir /srv/mail cd /srv/mail/ mkdir 0 1 2 3 4 5 6 7 8 9 a b c d e f g h i j k l m n o p q r s t u v w x y z chmod 771 /srv/mail chmod 751 /srv/mail/* For Dovecot >= 2.0 read the file Configure.Dovecot_2 Configuring Dovecot v1.x * /etc/dovecot/dovecot.conf # all your other settings #disable_plaintext_auth = no mail_location = maildir:~/Maildir first_valid_uid = 70000 first_valid_gid = 70000 protocol lda { postmaster_address = postmaster@YOUR-DOMAIN.TLD } auth default { mechanisms = cram-md5 login plain passdb sql { args = /etc/dovecot/dovecot-sql.conf } userdb sql { args = /etc/dovecot/dovecot-sql.conf } user = doveauth socket listen { master { path = /var/run/dovecot/auth-master mode = 0600 } client { path = /var/spool/postfix/private/dovecot-auth mode = 0660 user = postfix group = postfix } } } * /etc/dovecot/dovecot-sql.conf driver = pgsql connect = host=localhost dbname=mailsys user=dovecot password=$Dovecot_PASS default_pass_scheme = CRAM-MD5 password_query = SELECT userid AS "user", password FROM dovecotpassword('%Ln', '%Ld') WHERE %Ls user_query = SELECT home, uid, gid, mail FROM dovecotuser('%Ln', '%Ld') Provide a root SETUID copy of Dovecot's deliver agent for Postfix /!\ Only required with Dovecot v.1.x. With Dovecot >= v2.0 use Dovecot's lmtp! mkdir -p /usr/local/lib/dovecot chmod 700 /usr/local/lib/dovecot chown nobody /usr/local/lib/dovecot cp /usr/lib/dovecot/deliver /usr/local/lib/dovecot/ chown root:`id -g nobody` /usr/local/lib/dovecot/deliver chmod u+s,o-rwx /usr/local/lib/dovecot/deliver Start or restart Dovecot Configuring Postfix's master.cf /!\ Only required with Dovecot v.1.x. # Add Dovecot's deliver agent dovecot unix - n n - - pipe flags=DORhu user=nobody argv=/usr/local/lib/dovecot/deliver -f ${sender} -d ${user}@${nexthop} -n -m ${extension} Configuring Postfix's main.cf sql = pgsql:${config_directory}/ proxysql = proxy:${sql} # relocated users from the database #relocated_maps = ${proxysql}pgsql-relocated_maps.cf # transport settings from our database transport_maps = ${proxysql}pgsql-transport_maps.cf # virtual domains virtual_mailbox_domains = ${proxysql}pgsql-virtual_mailbox_domains.cf virtual_alias_maps = ${proxysql}pgsql-virtual_alias_maps.cf virtual_minimum_uid = 70000 virtual_uid_maps = ${sql}pgsql-virtual_uid_maps.cf virtual_gid_maps = ${sql}pgsql-virtual_gid_maps.cf virtual_mailbox_base = / virtual_mailbox_maps = ${proxysql}pgsql-virtual_mailbox_maps.cf # dovecot LDA (only recommended with Dovecot v1.x) #dovecot_destination_recipient_limit = 1 #virtual_transport = dovecot: # dovecot lmtp virtual_transport = lmtp:unix:private/dovecot-lmtp # dovecot SASL smtpd_sasl_type = dovecot smtpd_sasl_path = private/dovecot-auth smtpd_sasl_auth_enable = yes # Keep smtpd_sasl_local_domain identical to Dovecot's auth_default_realm: # empty. Both are empty by default. Let it commented out. # Read more at: http://wiki.dovecot.org/Authentication/Mechanisms/DigestMD5 #smtpd_sasl_local_domain = smtpd_sasl_security_options = noplaintext, noanonymous #smtpd_sasl_security_options = noanonymous #broken_sasl_auth_clients = yes smtpd_recipient_restrictions = permit_mynetworks permit_sasl_authenticated reject_unauth_destination Installing the Virtual Mail Manager and configure the rest Installing from Mercurial or vmm-x.y.z.tar.gz after cloning from the hg repo or extracting the archive change into the new directory and type: ./install.sh edit all the pgsql-*.cf files in /etc/postfix reload postfix # configure the Virtual Mail Manager # vmm.cfg(5) - configuration file for vmm # # For Dovecot v1.x use 'dovecot:' as domain.transport # When using Dovecot v2.x use 'lmtp:unix:private/dovecot-lmtp' as # domain.transport vmm configure # for help type # vmm(1) - command line tool to manage email domains/accounts/aliases vmm help vmm-0.6.1/ChangeLog0000644000175000017500000041400712033032424012260 0ustar pvopvo=== 0.6.1 === 2012-10-03 Pascal Volk * doc/web/source/conf.py, doc/web/source/download.rst, doc/web/source/howto/manage_accounts.rst, doc/web/source/installation/install_vmm.rst, doc/web/source/installation/postgresql_configuration.rst, doc/web/source/pgsql_set_permissionspermissions.rst, doc/web/source/release_history.rst, doc/web/source/substitutions.rst, doc/web/source/upgrade/0.5-0.6.rst: doc/web: Updated documentation for vmm-0.6.1. [f151defe7078] [tip] 2012-10-02 martin f. krafft * man/de/man1/vmm.1, man/man1/vmm.1: Add list* commands to manpages [ac27f89fad86] 2012-10-01 Pascal Volk * UPGRADE: UPGRADE: Added procedure for updating from v0.6.0. [e1fc4a3075de] 2012-09-30 Pascal Volk * update_config.py: update_config: Updated to work with version 0.6.0. Fixed some PEP8 related issues. [d7101e496795] 2012-09-29 Pascal Volk * doc/web/source/howto/general_subcommands.rst, doc/web/source/howto/manage_alias_addresses.rst, doc/web/source/howto/manage_catch-all_addresses.rst: doc/web: Updated and extended documentation. Updated description of subcommands aliasdelete and catchalldelete. Added description of subcommands listaddresses, listaliases, listrelocated and listusers. [e60b8ed5fd35] * VirtualMailManager/cli/clihelp.py: VMM/cli/clihelp: Added missing help messages. Added descriptions of subcommands listaddresses, listaliases, listrelocated and listusers. [d8b60e53334f] 2012-09-27 Pascal Volk * po/nl.po: po: Updated Dutch translation (translated by Erwin Poeze). [ca882a1ff90b] * VirtualMailManager/alias.py: VMM/alias: Load destination addresses sorted. This produces sorted output for the aliasinfo subcommand. [4ec5c015b7aa] * VirtualMailManager/alias.py, VirtualMailManager/catchall.py, VirtualMailManager/cli/clihelp.py, VirtualMailManager/cli/subcommands.py, VirtualMailManager/handler.py, man/de/man1/vmm.1, man/man1/vmm.1: VMM: {alias,catchall}delete: Accept multiple destinations. [d8736bb80bdc] 2012-09-24 Pascal Volk * po/nl.po: po: Updated Dutch translation (translated by Erwin Poeze). [9eecf0160c39] 2012-09-23 Pascal Volk * VirtualMailManager/cli/main.py: VMM/cli/main: Check if we have warnings before exiting. If so, print them and exit afterwards. [996bfcae49b0] 2012-09-16 Pascal Volk * VirtualMailManager/domain.py: VMM/domain: Enabled transport-validation. Validate the transport also in Domain.set_transport() and Domain.update_transport(). [5882bfdf83e8] * VirtualMailManager/account.py, VirtualMailManager/common.py: VMM: Moved transport-validation to common. [1ec3497a6733] 2012-09-15 Pascal Volk * VirtualMailManager/account.py: VMM/account: Fixed account's transport check. Make sure that 'virtual:' isn't used as transport when the mailbox- format is set to mdbox or sdbox. [7b3494f1a2ef] 2012-09-12 Pascal Volk * po/nl.po: po: Updated Dutch translation (translated by Erwin Poeze). [924535664ecc] 2012-09-05 Pascal Volk * VirtualMailManager/account.py, VirtualMailManager/catchall.py, VirtualMailManager/cli/subcommands.py, VirtualMailManager/common.py, VirtualMailManager/config.py, VirtualMailManager/domain.py, VirtualMailManager/handler.py: PEP8: Fixed all PEP8 related issues. [8e9b0046bc8f] 2012-09-03 Pascal Volk * po/vmm.pot: po: Regenerated vmm.pot [b6c4e77046b9] * VirtualMailManager/cli/clihelp.py, VirtualMailManager/cli/subcommands.py, VirtualMailManager/common.py: Added a few comments for the Translation Project. [56ec275911f2] * VirtualMailManager/catchall.py, VirtualMailManager/cli/clihelp.py, VirtualMailManager/cli/subcommands.py, VirtualMailManager/ext/postconf.py: VMM: Corrected/updated some msgids. Unified some messages and corrected a few typos. [0ed93eb8b364] 2012-09-02 Pascal Volk * VirtualMailManager/cli/subcommands.py: VMM/cli/subcommands: Corrected username's usage string. username's name argument is optional. [46454ff9d441] * VirtualMailManager/cli/clihelp.py, VirtualMailManager/cli/subcommands.py: VMM/cli: Activated help subcommand. Implemented Command.help_(), which reads the command's help message from cli.clihelp. [ab97727357a4] * VirtualMailManager/handler.py: VMM/handler: Fixed user{quota,services,transport} subcommands. The subcommands user{quota,services,transport} actually failed with the arguments '
domain'. This is part II of changeset a75923ce2842 [b994444f7dee] * doc/web/source/howto/manage_accounts.rst: doc/web: Added missing userservices description. Fixed order of subcommand listing. [7fdc4b2d4640] * man/man1/vmm.1: man: Typo-fix. [981f02b2d4c5] * VirtualMailManager/cli/subcommands.py: VMM/cli/subcommands: Corrected two usage strings. domainnote's and usernote's note argument is optional. [af88f01e5bfa] 2012-09-01 Pascal Volk * VirtualMailManager/cli/subcommands.py, VirtualMailManager/common.py: VMM: Decode localized numbers to the current encoding. That avoids UnicodeDecodeErrors since locale.format(%d) may return localized numbers, which contain NO-BREAK SPACE as the thousands separator. [463b10c258d9] 2012-08-28 Pascal Volk * VirtualMailManager/cli/subcommands.py: VMM/cli/subcommands: Don't mark empty strings as translatable. gettext('') returns the header entry with meta information, not the empty string. [0571f45501da] * doc/web/source/upgrade/0.5-0.6.rst: doc/web: Added description 'Upgrade to Dovecot ≧ v1.2.beta2'. [82e722f35aab] 2012-08-27 Pascal Volk * pgsql/dovecot_update_v1.2+.pgsql: pgsql: Added dovecot_update_v1.2+.pgsql. Use this file when you are upgrading your Dovecot installation to a version >= 1.2.beta2. [bb23693e5fc9] * VirtualMailManager/cli/subcommands.py: VMM/cli/subcommands: Reverted accidentally committed help() code. [c79fdce49908] * VirtualMailManager/cli/subcommands.py, pgsql/create_tables.pgsql: pgsql/create_tables.pgsql: Eliminated two unnecessary differences. Yeah, cosmetic only. [607aa5c2acc4] * pgsql/create_tables.pgsql, pgsql/update_tables_0.5.x-0.6.pgsql: pgsql: Don't select service_set.ssid in dovecotpassword() unconditional. This may cause an empty result, which will make logins impossible. [86b967c0f1a1] * VirtualMailManager/serviceset.py: VMM/serviceset: Fixed a conditional ProgrammingError. Affects only setups with Dovecot < v1.2.beta2. Actually the SQL query in method ServiceSet._load_by_services wasn't updated. Removed unnecessary code, which would have also produced a ProgrammingError, if it would have been correct. [cdbe200c0ecc] 2012-08-23 Pascal Volk * VirtualMailManager/cli/subcommands.py: VMM/cli/subcommands: Added 'missing' newline below domain notes. This patch contains also a small code cleanup and PEP8-fix. [3dc1764c23d2] 2012-08-19 Pascal Volk * doc/web/source/howto/manage_accounts.rst: doc/web: Added the transport filed to the usertransport example. [ef384bc8fde6] 2012-08-12 Pascal Volk * doc/web/source/howto.rst, doc/web/source/howto/general_subcommands.rst, doc/web/source/howto/manage_accounts.rst, doc/web/source/howto/manage_alias_addresses.rst, doc/web/source/howto/manage_alias_domains.rst, doc/web/source/howto /manage_catch-all_addresses.rst, doc/web/source/howto/manage_domains.rst, doc/web/source/howto/manage_relocated_users.rst, doc/web/source/index.rst, doc/web/source/installation/install_vmm.rst: doc/web: Added the 'Howto'. The reStructuredText version of vmm.1, in multiple parts. [2b165e90e225] * man/de/man1/vmm.1, man/man1/vmm.1: man/{de/}man1: A few more corrections. [9d343514b832] * man/de/man1/vmm.1, man/man1/vmm.1: man/{de/}man1: Small corrections for domaininfo's description. [60907fc875f7] * man/de/man5/vmm.cfg.5, man/man5/vmm.cfg.5: man/{de/}man5: s/Wiki/Homepage/g [dcde0010cdf8] 2012-08-11 Pascal Volk * man/de/man1/vmm.1, man/man1/vmm.1: man/{de/}man1: s/Wiki/Homepage/g [8767ec854084] 2012-08-08 martin f. krafft * VirtualMailManager/common.py: VMM/common: Improve search_address complexity Checking the dictionary (a hash) for existence of a key is likely to be O(log(n)), while checking a list is O(n). Therefore, to increase performance, this patch changes the check accordingly. [cd1200d06700] 2012-08-07 Pascal Volk * doc/web/source/_static/vmm.cfg: doc/web: Refreshed vmm.cfg [ffd24974ed68] * vmm.cfg: vmm.cfg: Updated/improved comments. [f9a2327f57a6] 2012-08-07 martin f. krafft * VirtualMailManager/common.py: If an alias has multiple destinations, multiple records exist, due to the nature of the database. address_list would then return the same alias multiple times, which does not add any information, eats screen space and is potentially confusing. Therefore, we SELECT DISTINCTly from the alias table. Signed-off-by: martin f. krafft --- VirtualMailManager/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) [a479c38931c4] * VirtualMailManager/common.py: Due to the UNION query in address_list, the assumption that the list of gids received from the database would be continuous does not hold. To prevent addresses for domains with multiple entry types from being listed, it is necessary to check the list of domain gids for every entry. Signed-off-by: martin f. krafft --- VirtualMailManager/common.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) [605f8c115711] 2012-07-29 Pascal Volk * doc/web/source/installation/dovecot_configuration.rst, doc/web/source/installation/postgresql_configuration.rst: doc/web: Emphasized a few lines of Dovecot/PostgreSQL configuration file excerpts. [9c138471d569] * .hgignore: .hgignore: "doc/build" is no longer required. [b62210b96ea7] * doc/web/Makefile, doc/web/source/_static/local.conf, doc/web/source/_static/vmm.cfg, doc/web/source/_static/vmm_logo.png, doc/web/source/_templates/layout.html, doc/web/source/conf.py, doc/web/source/download.rst, doc/web/source/ext_references.rst, doc/web/source/features.rst, doc/web/source/index.rst, doc/web/source/install.rst, doc/web/source/installation/dovecot_configuration.rst, doc/web/source/installation/install_vmm.rst, doc/web/source/installation/postfix_configuration.rst, doc/web/source/installation/postgresql_configuration.rst, doc/web/source/installation/prerequisites.rst, doc/web/source/installation/system_preparation.rst, doc/web/source/pgsql_set_permissionspermissions.rst, doc/web/source/release_history.rst, doc/web/source/substitutions.rst, doc/web/source/upgrade.rst, doc/web/source/upgrade/0.4.x-0.5.rst, doc/web/source/upgrade/0.5-0.6.rst, doc/web/source/vmm.cfg.rst: doc: Added source of http://vmm.localdomain.org/. [be0906181a10] * doc/Makefile, doc/api/Makefile, doc/api/source/conf.py, doc/api/source/index.rst, doc/api/source/vmm.rst, doc/api/source/vmm_alias.rst, doc/api/source/vmm_config.rst, doc/api/source/vmm_constants_error.rst, doc/api/source/vmm_emailaddress.rst, doc/api/source/vmm_errors.rst, doc/api/source/vmm_relocated.rst, doc/source/conf.py, doc/source/index.rst, doc/source/vmm.rst, doc/source/vmm_alias.rst, doc/source/vmm_config.rst, doc/source/vmm_constants_error.rst, doc/source/vmm_emailaddress.rst, doc/source/vmm_errors.rst, doc/source/vmm_relocated.rst: doc: Moved API documentation to doc/api. [20141b967c0b] 2012-07-22 Pascal Volk * INSTALL: INSTALL: Updated PostgreSQL configuration URL. Use ${sql} and ${proxysql} expressions in the main.cf description. [4f9079dd4b65] 2012-07-01 Pascal Volk * README: README: Substituted a few words. [0be27a75776a] 2012-06-30 Pascal Volk * README: README: Updated URL of the hg repository@sf.net. [f9e6e0a237f0] * man/man1/vmm.1: man: Deleted erroneous placed backslash. [9acf628bf0be] 2012-06-28 Pascal Volk * .hgtags: Added tag vmm-0.6.0 for changeset 3238c58d01ae [0643c25121b5] * ChangeLog, NEWS, VirtualMailManager/constants.py, setup.py: Released vmm-0.6.0 [3238c58d01ae] [vmm-0.6.0] === 0.6.0 === 2012-06-28 Pascal Volk * VirtualMailManager/Account.py, VirtualMailManager/Alias.py, VirtualMailManager/AliasDomain.py, VirtualMailManager/Config.py, VirtualMailManager/Domain.py, VirtualMailManager/EmailAddress.py, VirtualMailManager/Exceptions.py, VirtualMailManager/MailLocation.py, VirtualMailManager/Relocated.py, VirtualMailManager/Transport.py, VirtualMailManager/VirtualMailManager.py, VirtualMailManager/constants/ERROR.py, VirtualMailManager/constants/EXIT.py, VirtualMailManager/constants/VERSION.py, VirtualMailManager/constants/__init__.py, VirtualMailManager/ext/Postconf.py, pgsql /create_optional_types_and_functions-dovecot-1.2.x.pgsql, pgsql/create_optional_types_and_functions.pgsql, pgsql/update_tables_0.4.x-0.5.pgsql, pgsql/update_tables_0.5.x_for_dovecot-1.2.x.pgsql, pgsql/update_types_and_functions_0.5.x_for_dovecot-1.2.x.pgsql, postfix/pgsql-transport.cf, update_config_0.4.x-0.5.py: Merged changes from v0.6.x(28230a8230bf). [a4aead244f75] [tip] * Close v0.6.x branch. [28230a8230bf] * vmm: vmm: Don't insert the module directory into sys.path by default. Doesn't make much sense, if the code is installed inside sys.path. [54a84a311654] * COPYING, VirtualMailManager/__init__.py, VirtualMailManager/account.py, VirtualMailManager/alias.py, VirtualMailManager/aliasdomain.py, VirtualMailManager/cli/__init__.py, VirtualMailManager/cli/config.py, VirtualMailManager/cli/handler.py, VirtualMailManager/cli/main.py, VirtualMailManager/cli/subcommands.py, VirtualMailManager/common.py, VirtualMailManager/config.py, VirtualMailManager/constants.py, VirtualMailManager/domain.py, VirtualMailManager/emailaddress.py, VirtualMailManager/errors.py, VirtualMailManager/ext/__init__.py, VirtualMailManager/ext/postconf.py, VirtualMailManager/handler.py, VirtualMailManager/mailbox.py, VirtualMailManager/maillocation.py, VirtualMailManager/network.py, VirtualMailManager/password.py, VirtualMailManager/pycompat/__init__.py, VirtualMailManager/pycompat/hashlib.py, VirtualMailManager/quotalimit.py, VirtualMailManager/relocated.py, VirtualMailManager/serviceset.py, VirtualMailManager/transport.py, setup.py, update_config.py, vmm: Updated copyright notices to include the year 2012. [14abdd04ddf5] * TODO: TODO: Removed "listpwschemes" entry. Was done with changeset a64c1b5e08b4 [11ed67ee7ebb] * UPGRADE: UPGRADE: Updated documentation. [007786e58fb7] * VirtualMailManager/cli/subcommands.py, man/de/man1/vmm.1, man/man1/vmm.1: VMM/cli/subcommands: Replaced the keyword `default' by `domain'. Patch by Martin F. Krafft [a75923ce2842] 2012-06-27 Pascal Volk * Configure.Dovecot_2, INSTALL: INSTALL: Added description how to create additional dove* user/group. `doveauth' user and group for the authentication process and the group `dovemail' for mail related Dovecot processes. [55148bc6348e] * INSTALL: INSTALL: Updated documentation. [b17a9d7a59ae] * Configure.Dovecot_2, install.sh, upgrade.sh: Configure.Dovecot_2: Added configuration documentation for Dovecot_2 >= 2.0. [b5bb7b34e831] * VirtualMailManager/account.py, VirtualMailManager/cli/subcommands.py: VMM: The Python 2.4.x compatibility commit - part III. [b0165b7af7a3] * VirtualMailManager/account.py: VMM/account: Check the account's transport only when we have a transport. [2662f4e17eb4] * VirtualMailManager/account.py, VirtualMailManager/cli/subcommands.py, VirtualMailManager/handler.py: VMM: The Python 2.4.x compatibility commit. [cc0f67f99780] 2012-06-14 Pascal Volk * pgsql/set-permissions.py: pgsql/set-permissions.py: Extended permissions. Postfix needs read access on the postfix_gid view. Dovecot needs read permissions on both mailboxformat columns. [2fcf12fdecc2] * VirtualMailManager/handler.py: VMM/handler: Restored method Handler.domain_transport(). It went somehow lost with changeset 5806fb74130b. [1498abbb6c91] 2012-05-31 Pascal Volk * pgsql/set-permissions.py: pgsql: Added helper script: set-permissions.py. [fbbb16476c5b] 2012-04-15 martin f. krafft * TODO, VirtualMailManager/cli/handler.py, VirtualMailManager/cli/subcommands.py, VirtualMailManager/common.py, VirtualMailManager/constants.py, VirtualMailManager/handler.py: Provide list{addresses,aliases,users,relocated} subcommands The patch provides the list{addresses,aliases,users,relocated} subcommands to the UI. All commands actually take the same path in the code and only one query is run with different parameters for each case. There are still two shortcomings: 1. With alias domains, the output order is not as one might want it, e.g. foo@example.org bar@example.org foo@example.com bar@example.com when it should really be foo@ twice and then bar@ twice. I have not found a way to modify the SQL accordingly. 2. The SELECT queries for Accounts, Alias and Relocated are hard- coded in common.py. [499c63f52462] * man/de/man1/vmm.1, man/de/man5/vmm.cfg.5, man/man1/vmm.1, man/man5/vmm.cfg.5: man: escape hyphens Hyphens inside words must be spelt \- instead of just -. This patch fixes up the manpages. * * * . [a93671970617] * man/de/man1/vmm.1, man/man1/vmm.1: man: add 'catchall' to domaininfo Since the addition of catchall, the domaininfo subcommand can also take 'catchall' as a [detail] to limit the output of details to the catch-all aliases. Also, the number of catch-all destinations is now included in the simple domaininfo output. [6f2c41c3c7d6] 2012-04-15 Pascal Volk * man/de/man1/vmm.1, man/man1/vmm.1: man: vmm1: Use `fqdn' instead of `domain' in catchall* descriptions. Fixed a typo s/cadd/cad/. Replaced double quotes by \(dq. [107f7a3be9f5] * VirtualMailManager/cli/subcommands.py: VMM/cli/subcommands: Use the `fqdn' argument placeholder also for catch-all subcommands. [62f2daff853e] 2012-04-15 martin f. krafft * pgsql/create_tables-dovecot-1.2.x.pgsql, pgsql/create_tables.pgsql, pgsql/update_tables_0.5.x-0.6-dovecot-1.2.x.pgsql, pgsql/update_tables_0.5.x-0.6.pgsql: Fix transport_maps function for non-existent domains The postfix_transport_maps function had a bug causing 2012-04-15 17:40:22 CEST LOG: statement: SELECT transport FROM postfix_transport_map('logcheck', 'domine.madduck.net'); 2012-04-15 17:40:22 CEST ERROR: query returned no rows when the domain was not in the database. This would make did be NULL and make the query fail. This patch moves the tid query until after a check for did. If the latter is NULL, the function RETURNs (rather than fails). [867d950ce7b7] * pgsql/create_tables-dovecot-1.2.x.pgsql, pgsql/create_tables.pgsql, pgsql/update_tables_0.5.x-0.6-dovecot-1.2.x.pgsql, pgsql/update_tables_0.5.x-0.6.pgsql: Revert caching of destination interpolation Since 'destination' comes from the table in the query, it cannot be cached across queries! Doh! [44a808af6cf4] 2012-04-15 Pascal Volk * pgsql/update_tables_0.5.x-0.6-dovecot-1.2.x.pgsql, pgsql/update_tables_0.5.x-0.6.pgsql: pgsql: update_tables_*: Added missing `NOT' to `… tid DROP NOT NULL;'. [92ef34f07da3] * pgsql/create_tables-dovecot-1.2.x.pgsql, pgsql/create_tables.pgsql: pgsql: Create column `note' as text. [5020c56f9388] 2012-04-15 martin f. krafft * VirtualMailManager/common.py: Add docstring for common.format_domain_default [79f09cdd1a21] 2012-04-14 martin f. krafft * man/de/man1/vmm.1, man/man1/vmm.1: Document domainnote in manpages 5806fb74130b did not contain this, so I must have forgotten it. Oops. [47254b46c361] * TODO: Remove domain/account notes from TODO [ff805bd17817] * VirtualMailManager/account.py: Include account note in getuser output Modify the getuser output to include an account note, if one is present [1b3bdce0bf87] * VirtualMailManager/cli/subcommands.py: Display formatted notes in info output If the user/domain object has an attached note, this patch makes the userinfo/domaininfo output print the note, wrapped to the terminal size. [c3b98364f03d] * .hgignore: Ignore *.egg-status build directory created by setuptools [a582f1452bc0] * man/de/man1/vmm.1, man/man1/vmm.1: Add user/domainnote commands to manpages [a8ee0328f908] * VirtualMailManager/account.py, VirtualMailManager/cli/subcommands.py, VirtualMailManager/domain.py, VirtualMailManager/handler.py: Add note field to Account/Domain and CLI [5806fb74130b] * pgsql/create_tables-dovecot-1.2.x.pgsql, pgsql/create_tables.pgsql, pgsql/update_tables_0.5.x-0.6-dovecot-1.2.x.pgsql, pgsql/update_tables_0.5.x-0.6.pgsql: Modify SQL to add note field to domain_data and users [1f9ea5658627] 2012-04-14 Pascal Volk * man/de/man1/vmm.1, man/man1/vmm.1: man: vmm.1: subcommand's name argument is optional now. Based on martin f. krafft's v2 of changeset c340ba4bd544. [9a23043b0530] 2012-04-14 martin f. krafft * VirtualMailManager/cli/subcommands.py, VirtualMailManager/handler.py, man/de/man1/vmm.1, man/man1/vmm.1: Allow username to take no argument to erase value Once a username has been set, it could not be removed via the UI. Now, if no argument is passed to username, the value stored is replaced with NULL. [c340ba4bd544] * README: Add inheritance as a feature to README [e956ddc868c0] * VirtualMailManager/domain.py, man/de/man1/vmm.1, man/man1/vmm.1: Change 'force' argument to reset user records for tid/ssid/qid If domain{quotalimit,transport,serviceset} are run with 'force' and settings are inheritable from the domain, then rather than to copy the ID to the user table, the affected field in the user records should be set to NULL so that the domain default is used. [6a27c7529cd7] * VirtualMailManager/cli/subcommands.py, VirtualMailManager/handler.py, man/de/man1/vmm.1, man/man1/vmm.1: Change UI to accept 'default' to restore inherited values Instead of explicit values for tid/ssid/qid, the UI now accepts 'default', which removed user-pecific settings and hence causes domain defaults to be used. [2676dbf43e1c] 2012-04-13 martin f. krafft * VirtualMailManager/account.py, VirtualMailManager/cli/subcommands.py, VirtualMailManager/common.py: Modify userinfo output to indicate when domain defaults are displayed When Account instances reference NULL tid/qid/ssid, the data must come from the associated domain, and this should be indicated. For transport and services, this is easy to do as the string passed in the info dict can simply be modified. For quotalimit, however, another method must be used due to the CLI-side formatting. All approaches use a common formatter outsourced to the common.py file. [2bb40aaef94e] * VirtualMailManager/account.py: Modify Account class to handle NULL references This patch modifies the Account class to defer to using the associated domain's tid/ssid/qid fields if the per-instance fields are None/NULL. [cf3eb03c1c4f] * pgsql/create_tables-dovecot-1.2.x.pgsql, pgsql/create_tables.pgsql, pgsql/update_tables_0.5.x-0.6-dovecot-1.2.x.pgsql, pgsql/update_tables_0.5.x-0.6.pgsql: Modify SQL update script to accept NULL fields for users The value NULL in the qid/ssid/tid fields of the user table means that the value from the associated domain record should be used instead. This patch modifies the PL/pgSQL functions used by Dovecot and Postfix accordingly. [95dd123b552e] * man/de/man1/vmm.1, man/man1/vmm.1: man: Add examples for userservices [916b468cf994] 2012-04-14 Pascal Volk * VirtualMailManager/account.py, VirtualMailManager/cli/subcommands.py, VirtualMailManager/common.py, pgsql/create_tables-dovecot-1.2.x.pgsql, pgsql/create_tables.pgsql, pgsql/update_tables_0.5.x-0.6-dovecot-1.2.x.pgsql, pgsql/update_tables_0.5.x-0.6.pgsql: Reverted previous three changesets (e09139525580, 85517c8fde36, 3acbff727626) [4b8c3f51d7da] 2012-04-13 martin f. krafft * VirtualMailManager/account.py, VirtualMailManager/cli/subcommands.py, VirtualMailManager/common.py: Modify userinfo output to indicate when domain defaults are displayed When Account instances reference NULL tid/qid/ssid, the data must come from the associated domain, and this should be indicated. For transport and services, this is easy to do as the string passed in the info dict can simply be modified. For quotalimit, however, another method must be used due to the CLI-side formatting. All approaches use a common formatter outsourced to the common.py file. [e09139525580] * VirtualMailManager/account.py: Modify Account class to handle NULL references This patch modifies the Account class to defer to using the associated domain's tid/ssid/qid fields if the per-instance fields are None/NULL. [85517c8fde36] * pgsql/create_tables-dovecot-1.2.x.pgsql, pgsql/create_tables.pgsql, pgsql/update_tables_0.5.x-0.6-dovecot-1.2.x.pgsql, pgsql/update_tables_0.5.x-0.6.pgsql: Modify SQL update script to accept NULL fields for users The value NULL in the qid/ssid/tid fields of the user table means that the value from the associated domain record should be used instead. This patch modifies the PL/pgSQL functions used by Dovecot and Postfix accordingly. [3acbff727626] 2012-04-14 martin f. krafft * TODO: Add list* subcommands to TODO [3ffe4ee3740f] * TODO: add domain/account notes to TODO [f494a593c674] 2012-04-13 martin f. krafft * TODO: Add my WIP items to the TODO list [dc66f66c51cf] * pgsql/create_tables-dovecot-1.2.x.pgsql, pgsql/create_tables.pgsql, pgsql/update_tables_0.5.x-0.6-dovecot-1.2.x.pgsql, pgsql/update_tables_0.5.x-0.6.pgsql: Cache interpolated destination Instead of running the string modification function for destination interpolation twice, store the result once and use the stored result subsequently. [75d1c0d6bb8f] 2012-04-12 Pascal Volk * man/man1/vmm.1: man: Use example.com in catchallinfo example output. [a0a27688e616] * README: README: reStructuredText fix: ERROR/3 and WARNING/2. [b1fd6f08f369] 2012-04-11 martin f. krafft * README, man/de/man1/vmm.1, man/man1/vmm.1, pgsql/create_tables- dovecot-1.2.x.pgsql, pgsql/create_tables.pgsql, pgsql/update_tables_0.5.x-0.6-dovecot-1.2.x.pgsql, pgsql/update_tables_0.5.x-0.6.pgsql: Enable interpolation of alias destinations This patch modifies the virtual_alias_maps function so that the destination address is parsed for the place holders %n, %d and %=, which are replaced with the localpart, the domain or the full address with '@' replaced by '=' of the queried key. In combination with alias domains, this allows for domain-specific recipients. E.g. given example.org and its alias domain example.com, defining an alias postmaster@example.org → postmaster+%d@example.org will cause mail to postmaster@example.*com* to go to postmaster+example.*com*@example.org. [5ec2068d02af] * README: Add catch-all aliases to README/features [88466a6ba3ae] 2012-04-10 martin f. krafft * man/de/man1/vmm.1, man/man1/vmm.1: Add catchall subcommands to manpages [b2084e7f6854] * VirtualMailManager/cli/subcommands.py, VirtualMailManager/domain.py, pgsql/create_tables-dovecot-1.2.x.pgsql, pgsql/create_tables.pgsql, pgsql/update_tables_0.5.x-0.6-dovecot-1.2.x.pgsql, pgsql/update_tables_0.5.x-0.6.pgsql: Include catch-all count in domaininfo output [09fa019bb330] * pgsql/create_tables-dovecot-1.2.x.pgsql, pgsql/create_tables.pgsql, pgsql/update_tables_0.5.x-0.6-dovecot-1.2.x.pgsql, pgsql/update_tables_0.5.x-0.6.pgsql: Make PL/pgSQL function feed back identity for mailboxes/relocated when there are catchall destinations. Without catchall aliases, if no virtual_alias matches, the query can just return NULL and Postfix will later check mailboxes/relocated for the address to rewrite. However, since virtual aliases are handled long before mailboxes/relocated, a catchall alias would also catch mail to mailboxes and relocated addresses, which we do not want. The way to tell postfix to keep delivering is for the virtual alias map to return the search key itself (identity function). This patch changes the postfix_virtual_alias_maps Pl/pgSQL function to do exactly that, but only if there are catchall destinations defined for the domain in question — otherwise it returns NULL when no match is found. [d863a44a6353] * VirtualMailManager/catchall.py: Fix syntax errors [557c4703986c] * pgsql/create_tables-dovecot-1.2.x.pgsql, pgsql/create_tables.pgsql, pgsql/update_tables_0.5.x-0.6-dovecot-1.2.x.pgsql, pgsql/update_tables_0.5.x-0.6.pgsql: Modify virtual_alias_maps function to check mailboxes/relocated first [821d3ffaaaca] * pgsql/create_tables-dovecot-1.2.x.pgsql, pgsql/create_tables.pgsql, pgsql/update_tables_0.5.x-0.6-dovecot-1.2.x.pgsql, pgsql/update_tables_0.5.x-0.6.pgsql: Make virtual_alias_maps function search catchall when no aliases are found [0244f1344b04] * VirtualMailManager/cli/subcommands.py, VirtualMailManager/domain.py, VirtualMailManager/handler.py: Add catchall info to domain_info output [1fa354bbdb90] * VirtualMailManager/catchall.py: Correct syntax errors [1a9247e9763f] 2012-04-09 martin f. krafft * VirtualMailManager/handler.py: Fixup catchall_info to return iterator, not self [18870919ec48] * VirtualMailManager/catchall.py: Correct syntax errors [2aae58cddfa9] * VirtualMailManager/cli/subcommands.py: Teach CLI about catchall* commands [fb0ffde628d6] * VirtualMailManager/handler.py: Provide catchall_* methods to the Handler class [3da8c919584f] * VirtualMailManager/catchall.py: First version of a CatchallAlias class I based the CatchallAlias class in catchall.py heavily on the Alias class, but by copy, not deriving. The two are functionally related, but the implementations are too different because CatchallAliases have no localpart. [f180ead60568] * pgsql/create_tables-dovecot-1.2.x.pgsql, pgsql/create_tables.pgsql, pgsql/update_tables_0.5.x-0.6-dovecot-1.2.x.pgsql, pgsql/update_tables_0.5.x-0.6.pgsql: Add database table for catchall destinations Catch-all aliases are stored for a domain. Since there can be multiple destinations per domain, this is done using a 1:n relation on the gid. This commit extends the SQL schema definition and also provides appropriate upgrade DDL. [492c179094c9] 2012-04-08 Pascal Volk * pgsql/create_optional_types_and_functions-dovecot-1.2.x.pgsql, pgsql/create_optional_types_and_functions.pgsql, pgsql /create_tables-dovecot-1.2.x.pgsql, pgsql/create_tables.pgsql, pgsql/update_tables_0.5.x-0.6-dovecot-1.2.x.pgsql, pgsql/update_tables_0.5.x-0.6.pgsql, pgsql/update_types_and_functions_0.5.x-0.6-dovecot-1.2.x.pgsql, pgsql/update_types_and_functions_0.5.x-0.6.pgsql: pgsql: Merged types and functions into the create_tables… files. [e1b32377032f] * postfix/pgsql-relocated_maps.cf, postfix/pgsql-transport_maps.cf, postfix/pgsql-virtual_alias_maps.cf, postfix/pgsql- virtual_mailbox_maps.cf, postfix/pgsql-virtual_uid_maps.cf: postfix: Removed VIEW-based comments from configuration files. [f2387d60624b] * pgsql/create_tables-dovecot-1.2.x.pgsql, pgsql/create_tables.pgsql, pgsql/update_tables_0.5.x-0.6-dovecot-1.2.x.pgsql, pgsql/update_tables_0.5.x-0.6.pgsql: pgsql: Removed most of the VIEWs. There are only two views left for now: - postfix_gid - vmm_domain_info All other views have a faster replacement: plpgsql functions. [5ccc9c6e5193] 2012-04-08 martin f. krafft * .hgignore: Ignore even more Debian-build-related files [23d04d1f29e8] * .hgignore: Ignore more Debian-build-related files Debian produces log and debhelper files in debian/* that do not need to be known to mercurial. Hence, this patch adds the globs to .hgignore. [419c929739fd] * TODO: Add non-root TODO [5b9a03762813] 2012-04-07 Pascal Volk * VirtualMailManager/config.py: VMM/config: Fixed a typo: s/DB_MUDULES/DB_MODULES/g. [17f2c5b5098e] 2012-04-07 martin f. krafft * .hgignore: Ignore build files related to Debian packaging During Debian packaging, the package is built in ./debian/vmm/, and quilt is used (./.pc/). Both directories contain only generated files and should hence be ignored from the perspective of Mercurial. [612a60e9d558] * .hgignore: Instruct mercurial to ignore build directory Python's distutil builds the package into ./build/, which can and should be ignored with respect to the repository. This patch simply adds the appropriate line to .hgignore. [2c5e2613eca8] 2012-04-06 Pascal Volk * VirtualMailManager/handler.py: VMM/handler: Create domain directory with umask 0022. [30365a87650d] 2012-04-07 martin f. krafft * VirtualMailManager/domain.py: Modify address check query to obtain well-defined result The way in which UNION does not yield the desired result, because (a) UNION merges results and (b) the result order is undefined. This patch changes the query to select the counts as columns and hence provides a well-defined order. [e5c2b3647971] 2012-04-06 Pascal Volk * postfix/pgsql-smtpd_sender_login_maps.cf: postfix: Uncommented the query in pgsql-smtpd_sender_login_maps.cf. [320531aa1280] * postfix/pgsql-transport.cf, postfix/pgsql-transport_maps.cf: postfix: Renamed pgsql-transport.cf to pgsql-transport_maps.cf. Now it matches pgsql-${postfix-parameter}.cf. [9cdc6ef83265] 2012-03-29 Pascal Volk * po/fr.po: po: Updated French translation (translated by Dimitri Duc). [e0441b07bccc] 2012-03-13 Pascal Volk * po/fi.po: po: Updated Finnish translation (translated by Jorma Karvonen). [b8d5564b4e42] 2011-12-13 Pascal Volk * VirtualMailManager/cli/subcommands.py: VMM/cli/subcommands: Fixed a TypeError in user_info(). [fb0af82ab4af] 2011-12-05 Pascal Volk * postfix/pgsql-relocated_maps.cf, postfix/pgsql-transport.cf, postfix /pgsql-virtual_alias_maps.cf, postfix/pgsql-virtual_mailbox_maps.cf, postfix/pgsql-virtual_uid_maps.cf: postfix: Use pgsql functions in query templates where possible. [ccdfbbb1bb01] * pgsql/create_optional_types_and_functions-dovecot-1.2.x.pgsql, pgsql/create_optional_types_and_functions.pgsql: pgsql: Updated required privileges for function dovecotpassword. [10e9b4855173] 2011-11-27 Pascal Volk * README: README: Added section `Installation Prerequisites'. Reworked some parts. [35395b49bc44] 2011-11-26 Pascal Volk * update_config.py, upgrade.sh: update_config: Write the old package install dir to a tmp file. upgrade: Remove old CamelCase files. Removed some unused variables. [fb210a116f18] 2011-11-13 Pascal Volk * VirtualMailManager/serviceset.py: VMM: The pyPgSQL compatibility commit. [428ee9cdf1b2] * VirtualMailManager/cli/subcommands.py, VirtualMailManager/config.py: VMM: The Python 2.4.x compatibility commit. [90d69ae4f40d] * VirtualMailManager/relocated.py: VMM/relocated: Don't accept something@localhost as destination. [099de308fd98] * man/de/man1/vmm.1, man/man1/vmm.1: man1: Updated configuration related descriptions/examples, accordingly to commit 4ff0fa3ba0fa. [b33bdc0c3669] 2011-11-12 Pascal Volk * VirtualMailManager/constants.py, man/de/man1/vmm.1, man/de/man5/vmm.cfg.5, man/man1/vmm.1, man/man5/vmm.cfg.5, setup.py: Updated the author's e-mail address. [6278a7a2a476] 2011-11-12 Pascal Volk * man/de/man1/vmm.1, man/de/man5/vmm.cfg.5, man/man1/vmm.1, man/man5/vmm.cfg.5: man: Updated the URL of the bug tracker. [86fa217cba11] 2011-11-11 Pascal Volk * man/de/man5/vmm.cfg.5, man/man5/vmm.cfg.5: man5: Updated the `usable password schemes' part. [c30ce0e079b6] 2011-11-10 Pascal Volk * man/de/man1/vmm.1, man/man1/vmm.1: man1: Copied new formatted/translated command output into the examples. [e321b764268e] 2011-11-09 Pascal Volk * VirtualMailManager/cli/subcommands.py: VMM/cli/subcommands: Small improvement of domaininfo's output. Pointed out that the displayed quota limit is valid per user. [4353981e3a7f] * man/de/man1/vmm.1, man/man1/vmm.1: man1: Removed obsolete commands and added new commands. [9b9cccebccf0] * man/de/man1/vmm.1: man/de/man1: Removed translated argument names. [8f59a2f539e7] 2011-11-08 Pascal Volk * VirtualMailManager/cli/subcommands.py: VMM/cli/subcommands: Use the common output formatting in list_pwschemes(). [5d0114f7bb99] * VirtualMailManager/handler.py: VMM/handler: Added a comment for the Translation Project. [807ad4df0774] * VirtualMailManager/cli/subcommands.py: VMM/cli/subcommands: Subcommand argument placeholders are no longer translatable. [1ebb6b88f589] 2011-11-07 Pascal Volk * po/de.po: po: Updated German translation (translated by Mario Blättermann). [89bf9389d62f] * po/vmm.pot: po: Updated address for message-id bug reports. Looks like as sf.net would have horrible configured mail servers. :-/ [b4d21e8bfa00] * merged changes from default(c0e1fb1b0145) [863c691d7d14] * po/fi.po: po: Added Finnish translation to the repository. Many thanks to Jorma Karvonen from the Translation Project for the work. [c0e1fb1b0145] * VirtualMailManager/password.py: VMM/password: list_schemes(): report suffixes with a leading dot. [7d4d79ff08d0] * po/de.po, po/vmm.pot: po: Updated messages template and German translation. [b175c9f62602] 2011-11-06 Pascal Volk * VirtualMailManager/cli/subcommands.py: VMM/cli/subcommands: Added new subcommand `listpwschemes' (lp). Marked version()'s ctx argument as unused. [a64c1b5e08b4] * VirtualMailManager/password.py: VMM/password: Added function list_schemes(). [cabdf94ec580] * man/de/man5/vmm.cfg.5, man/man5/vmm.cfg.5: man5: Moved some option descriptions to section doamin, accordingly to commit 4ff0fa3ba0fa. Added a brief explanation of which settings will be applied in which situations. [e57dd007d69a] 2011-11-05 Pascal Volk * update_config.py: update_config: Updated, so that the previous config modifications will be applied to 0.5.2 configurations files. [033a0436894f] * VirtualMailManager/cli/subcommands.py, VirtualMailManager/config.py, VirtualMailManager/handler.py, vmm.cfg: VMM/config: Moved some options to section `domain': old new ------------------------------------------------------------ account.imap -> domain.imap account.pop3 -> domain.pop3 account.sieve -> domain.sieve account.smtp -> domain.account misc.quota_bytes -> domain.quota_bytes misc.quota_messages -> domain.quota_messages misc.transport -> domain.transport [4ff0fa3ba0fa] * VirtualMailManager/__init__.py: VMM: Show a warning when a unsupported locale setting was detected. [2019aa415dcd] * VirtualMailManager/cli/subcommands.py: VMM/cli/subcommands: Small improvement in domain_add(). Inform the admin, when the postmaster account will be auto-created. [7847f949d0a2] * VirtualMailManager/cli/subcommands.py: VMM/cli/subcommands: Fixed a small failure in alias_info(). [b15960e9c53c] * TODO: TODO: updated … [7d27627c7fb0] * VirtualMailManager/domain.py: VMM/domain: Extended Domain._chk_state() to support both states. [f2934105181b] 2011-11-04 Pascal Volk * VirtualMailManager/constants.py: VMM/constants: Cleared out obsolete error codes. [63ae77f05088] * VirtualMailManager/account.py: VMM/account: Removed unused import. [2408a3cd4bea] * VirtualMailManager/maillocation.py, VirtualMailManager/quotalimit.py: VMM/{maillocation,quotalimit}: Unified object initialization code. Simply raise a ValueError for wrong values (unknown IDs). [fd4aa073015f] * VirtualMailManager/errors.py, VirtualMailManager/transport.py: VMM/transport: Reworked Transport initialization. Set the transport id only if we could find a transport with that id. Raise a ValueError instead of a translatable TransportError(VMMError). VMM/errors: Deleted class TransportError, it is no longer needed. [6826acb9b0a7] * VirtualMailManager/cli/subcommands.py: VMM/cli/subcommands: domainservices: Don't fail when neither a service nor the keyword force was provided. [45c917e6a386] * VirtualMailManager/cli/subcommands.py, VirtualMailManager/domain.py, VirtualMailManager/handler.py: VMM/domain: Extended/updated Domain.get_info() in order to: - include active service(s) || None - use more understandable English terms VMM/cli/subcommands: Updated (order of) keys of `domaininfo' output. .`userinfo' will always show `sieve', no longer `managesieve'. [c31d604e26d3] 2011-11-03 Pascal Volk * VirtualMailManager/cli/subcommands.py: VMM/cli/subcommands: Small indent fix. [18b75e6a39d5] * VirtualMailManager/cli/subcommands.py: VMM/cli/subcommands: Added subcommand domainservices (ds). Replaced subcommands userdisable and userenable by userservices (us). [b8c94e06cd46] 2011-11-02 Pascal Volk * VirtualMailManager/account.py, VirtualMailManager/handler.py: VMM/account: Integrated class ServiceSet in class Account. Replaced methods Account.enable() and Account.disable() by Account.update_serviceset(). VMM/handler: Replaced methods Handler.user_disable() and Handler.user_enable() by Handler.user_services() [95275b61ff8a] 2011-11-01 Pascal Volk * VirtualMailManager/serviceset.py: VMM/serviceset: Added @property ServiceSet.services -> dict. [e2b9e3de2b51] * VirtualMailManager/account.py: VMM/account: Sorted imports … [abcd73f5e980] * VirtualMailManager/domain.py, VirtualMailManager/handler.py: VMM/domain: Integrated class ServiceSet in class Domain. VMM/handler: Extended Handler.domain_add() and added Handler.domain_services(). Removed unused domain.get_gid import. [1af5fe0683ce] * VirtualMailManager/serviceset.py: VMM/serviceset: Use SERVICES inside of ServiceSet - avoid slicing. [1a08fe35b496] 2011-10-31 Pascal Volk * VirtualMailManager/serviceset.py: VMM/serviceset: Added module constant SERVICES. [39036f5e6387] 2011-10-30 Pascal Volk * VirtualMailManager/serviceset.py: VMM: Added new module serviceset - provides class ServiceSet. [e35efe931af3] 2011-10-28 Pascal Volk * pgsql/create_optional_types_and_functions-dovecot-1.2.x.pgsql, pgsql/create_optional_types_and_functions.pgsql, pgsql /create_tables-dovecot-1.2.x.pgsql, pgsql/create_tables.pgsql, pgsql/update_tables_0.5.x-0.6-dovecot-1.2.x.pgsql, pgsql/update_tables_0.5.x-0.6.pgsql, pgsql/update_types_and_functions_0.5.x-0.6-dovecot-1.2.x.pgsql, pgsql/update_types_and_functions_0.5.x-0.6.pgsql: pgsql: Added new table `service_set'. Moved columns `smtp', `pop3' `imap' and `sieve' from the `users' table to the new `service_set' table. [9823548b2717] 2011-10-26 Pascal Volk * update_config.py: update_config: Set database.module to pyPgSQL only when psycopg2 is unavailable. [d296a020f440] * README: README: Added contet. [ee6f8ac06197] * man/de/man1/vmm.1.rst, man/de/man5/vmm.cfg.5.rst, man/substitute_links.rst, man/substitute_links_1.rst, man/substitute_links_5.rst: man: Removed reStructuredText files. [3ca31d080432] * man/de/man1/vmm.1, man/de/man5/vmm.cfg.5: man: Reworked/updated German manual pages. [c2543ddde9f5] 2011-10-25 Pascal Volk * man/man1/vmm.1: man: Added missing `.RE' macro to sub section userinfo (ui). [ad7cfbfbc68d] 2011-10-16 Pascal Volk * po/de.po: po: Fixed 2 fatal msgfmt errors inde.po. [fdd05a757f43] * po/de.po: po: Updated German translation. [c11edcfc6e5f] * VirtualMailManager/cli/subcommands.py, po/vmm.pot: Messages: Replaced some 'Available' by 'Existing'. [9842650569c2] * po/vmm.pot: po: Regenerated vmm.pot [0d8e7977ae63] 2011-09-03 Pascal Volk * VirtualMailManager/emailaddress.py: VMM/emailaddress: Class DestinationEmailAddress accepts also something@localhost addresses now. [8e41e77b84e6] * VirtualMailManager/handler.py: VMM/handler: Handler.alias_delete() also use class DestinationEmailAddress for the destination address, instead of class EmailAddress. [933b9debbec1] * VirtualMailManager/alias.py: VMM/alias: Alias.del_destination() corrected the value assignment in the error message's dictionary. [8394e222aba3] * VirtualMailManager/alias.py: VMM/alias: Alias._load_dests() use class DestinationEmailAddress for destination addresses, instead of class EmailAddress. [46c296c6e231] 2011-07-31 Pascal Volk * VirtualMailManager/cli/subcommands.py: VMM/cli/subcommands: user_add() show only generated passwords - not the given. [a43a2e20de71] 2011-03-10 Pascal Volk * VirtualMailManager/cli/handler.py, VirtualMailManager/ext/postconf.py, VirtualMailManager/handler.py, VirtualMailManager/mailbox.py: VMM: Deleted a few unused variables. [33a45e4c80c4] 2011-03-05 Pascal Volk * VirtualMailManager/network.py: VMM/network: Added new module network. [ff2a61e155db] 2011-02-27 Pascal Volk * VirtualMailManager/cli/main.py, VirtualMailManager/cli/subcommands.py: VMM/cli/subcommands: Added function update_cmd_map(). So the items will be added to the cmd_map when gettext's _() has been installed -> translatable subcommand descriptions. [d4a341248500] 2011-02-24 Pascal Volk * po/vmm.pot: po: Regenerated vmm.pot [241b192bfcc8] * VirtualMailManager/cli/subcommands.py: VMM/cli/subcommands: The keyword 'force' is no longer translatable. [542a4d23a9f0] * VirtualMailManager/account.py, VirtualMailManager/alias.py, VirtualMailManager/aliasdomain.py, VirtualMailManager/cli/__init__.py, VirtualMailManager/cli/handler.py, VirtualMailManager/cli/main.py, VirtualMailManager/cli/subcommands.py, VirtualMailManager/domain.py, VirtualMailManager/emailaddress.py, VirtualMailManager/ext/postconf.py, VirtualMailManager/handler.py, VirtualMailManager/password.py, VirtualMailManager/relocated.py: VMM: Updated some messages. (doesn't, isn't) -> (does not, is not) Added missing dots at the end of some sentences. [8209da83e256] * man/man1/vmm.1, man/man5/vmm.cfg.5: man: Replaced unnecessary \(aq glyphs in man1/vmm.1. Updated COPYING section in man1/vmm.1 and man5/vmm.cfg.5. [f32b323fd347] 2011-02-22 Pascal Volk * VirtualMailManager/mailbox.py: VMM/mailbox: Use the correct mailbox hierarchy separator in Mailbox.add_boxes() for mdbox and sdbox mailbox format. [c7a963e6cf6e] * VirtualMailManager/cli/subcommands.py, VirtualMailManager/common.py: VMM: Report quota usage/limit/percentage values formatted according to the current LC_ALL setting. [ae1a8428298c] * VirtualMailManager/cli/subcommands.py: VMM/cli/subcommands: Improved legibility of domaininfo's "quota limit" output a little bit. [e5b3b225bd5b] * man/man5/vmm.cfg.5, man/man5/vmm.cfg.5.rst: man/man5: Reworked/updated vmm.cfg.5. Removed vmm.cfg.5.rst. [65687300ba19] * man/man1/vmm.1: man/man1: Small syntax fixes in vmm.1. [1ca025a5ce0c] 2011-02-21 Pascal Volk * man/man1/vmm.1, man/man1/vmm.1.rst: man/man1: Reworked/updated vmm.1. Removed vmm.1.rst. reStructuredText was nice to edit but the generated output … [2bc9c36c1387] 2011-02-18 Pascal Volk * VirtualMailManager/cli/subcommands.py: VMM/cli/subcommands: Update also the RunContext's subcommand if 'plan A' in {,alias}domain_info() fails. [54a89c19e534] * VirtualMailManager/cli/subcommands.py: VMM/cli/subcommands: Fixed a msgid issue (format string with unnamed arguments). [f6e55b27fe07] * VirtualMailManager/handler.py, VirtualMailManager/quotalimit.py: VMM: Solve the default "misc.quota_bytes = '0'" problem where it may occur. [3162ff959375] * VirtualMailManager/cli/subcommands.py: VMM/cli/subcommands: Made argument names translatable. [58e23bd7c97f] 2011-02-14 Pascal Volk * man/de/man1/vmm.1.rst, man/man1/vmm.1.rst, man/substitute_links_1.rst: man1/vmm.1.rst: Added subcommands domainquota and userquota. Updated description of subcommand aliasadd. [da80de422b3c] 2011-02-13 Pascal Volk * VirtualMailManager/account.py, VirtualMailManager/domain.py: VMM/{account,domain}: Added a versions check to the update_quotalimit methods of classes Account and Domain. [0c52094447b0] * VirtualMailManager/cli/subcommands.py: VMM/cli/subcommands: Implemented subcommands domainquota and userquota. [a4f5d4cd886d] * VirtualMailManager/domain.py: VMM/domain: Execute the database updates of Domain.update_{quotalimit,transport}() when argument force is True, even when the 'new' setting is the same as the current one. [8984b1f4e6e3] 2011-02-12 Pascal Volk * VirtualMailManager/cli/subcommands.py: VMM/cli/subcommands: Divided 'quota usage' from userinfo subcommand into 'quota storage' and 'quota messages'. [00a8c12a3da3] * VirtualMailManager/quotalimit.py: VMM/quotalimit: Don't fail in QuotaLimit.__init__, if we get the deafult misc.quota_bytes value: '0'. [0512d940918f] * VirtualMailManager/common.py: VMM/common: Reworked human_size() once more. Return bytes w/o prefix. Use translatable $FOO_bibyte prefixes, e.g., MiB. [fb22773f7a85] * VirtualMailManager/cli/subcommands.py: VMM/cli/subcommands: Added quota limit/usage output to {domain,user}info subcommands. [3e5ed678d535] 2011-02-11 Pascal Volk * VirtualMailManager/cli/subcommands.py: VMM/cli/subcommands: config_get: Print configured misc.quota_bytes in human readable format. [cf661a40266d] * VirtualMailManager/common.py: VMM/common: human_size() size argument can be also a string. Because the default value of misc.quota_bytes is '0', a string in order to accept also settings like '500M'. [7f931c1ca059] * VirtualMailManager/common.py: VMM/common: human_size accept also 0 as size. [0a13849243f2] * VirtualMailManager/common.py: VMM/common: Small code cleanup in human_size(). [d3a3c6155879] * VirtualMailManager/common.py: VMM/common: Added function human_size(). [fb2ba1456bc5] 2011-02-10 Pascal Volk * VirtualMailManager/domain.py: VMM/domain: Adjusted Domain.get_info() to the updated vmm_domain_info view. [ffce67e3c6eb] * pgsql/create_tables-dovecot-1.2.x.pgsql, pgsql/create_tables.pgsql, pgsql/update_tables_0.5.x-0.6-dovecot-1.2.x.pgsql, pgsql/update_tables_0.5.x-0.6.pgsql: pgsql: Updated view vmm_domain_info. No longer select data we have already. [8217ddd5220d] * VirtualMailManager/account.py, VirtualMailManager/domain.py, VirtualMailManager/handler.py: VMM/{account,domain,handler}: Added quota limit support. A few small modifications in class Account. [660b42391c8e] 2011-02-09 Pascal Volk * VirtualMailManager/account.py: VMM/account: Removed unused method Account.set_transport(). [5f7e9f778b29] 2011-02-08 Pascal Volk * pgsql/create_tables-dovecot-1.2.x.pgsql, pgsql/create_tables.pgsql, pgsql/update_tables_0.5.x-0.6-dovecot-1.2.x.pgsql, pgsql/update_tables_0.5.x-0.6.pgsql: pgsql: Added quotalimit's bytes and messages to view vmm_domain_info. [dd95ed5bc9d2] * pgsql/create_tables-dovecot-1.2.x.pgsql, pgsql/create_tables.pgsql, pgsql/update_tables_0.5.x-0.6-dovecot-1.2.x.pgsql, pgsql/update_tables_0.5.x-0.6.pgsql: pgsql/{create,update}_tables*: Added the ON DELETE CASCADE clause to table userquota{,_11}'s foreign-key constraint fkey_userquota_uid_users. [05dc4e1f8dff] 2011-02-07 Pascal Volk * VirtualMailManager/mailbox.py: VMM/mailbox: Added missing dummy translator (_) and comment for method Mailbox._add_boxes. [b7854259ad74] * VirtualMailManager/quotalimit.py: VMM/quotalimit: Added new module quotalimit to the repository. [0cae9989395b] 2011-02-06 Pascal Volk * VirtualMailManager/config.py, man/de/man5/vmm.cfg.5.rst, man/man5/vmm.cfg.5.rst, vmm.cfg: VMM/config: Added quota_bytes and quota_messages settings. Updated …/man5/vmm.cfg.5 and vmm.cfg. [d3a97f7fb98a] 2011-02-04 Pascal Volk * VirtualMailManager/common.py: VMM/common: Added function size_in_bytes(). [ac5ac03b58da] * pgsql/create_optional_types_and_functions-dovecot-1.2.x.pgsql, pgsql/create_optional_types_and_functions.pgsql, pgsql /create_tables-dovecot-1.2.x.pgsql, pgsql/create_tables.pgsql, pgsql/update_tables_0.5.x-0.6-dovecot-1.2.x.pgsql, pgsql/update_tables_0.5.x-0.6.pgsql, pgsql/update_types_and_functions_0.5.x-0.6-dovecot-1.2.x.pgsql, pgsql/update_types_and_functions_0.5.x-0.6.pgsql: pgsql/*: Added tables, triggers and functions for quota support. [5e6bcb2e010e] 2011-02-02 Pascal Volk * VirtualMailManager/handler.py: VMM:/handler: Fixed typo introduced with the previous commit. [98223e5c95e0] * VirtualMailManager/handler.py: VMM/handler: Handler._make_domain_dir(): Check if the domain directory exists before calling os.mkdir(). [5aaf2dd6b146] * VirtualMailManager/alias.py, VirtualMailManager/aliasdomain.py, VirtualMailManager/domain.py, VirtualMailManager/relocated.py, VirtualMailManager/transport.py: VMM/*: Use target column names in all INSERT statements. In order to avoid trouble after the definition of a table was changed. [7518d927d443] 2011-02-01 Pascal Volk * INSTALL: INSTALL: Updated "user_query". [af56cee51e67] 2011-01-28 Pascal Volk * VirtualMailManager/mailbox.py: VMM/mailbox: Be more detailed if doveadm couldn't create mailboxes. [920cb090eb09] 2011-01-20 Pascal Volk * VirtualMailManager/cli/subcommands.py: VMM/cli/subcommands: Update also the RunContext's subcommand if 'plan A' in {alias,relocated,user}_info() fails. [2af61bc06215] * VirtualMailManager/cli/handler.py, VirtualMailManager/handler.py: VMM/{,cli/}handler: Moved the 'address-in-use check' (introduced with changeset ef99be5b7ec0) to {alias,relocated,user}_add(). [4cbaf8d229f9] 2011-01-19 Pascal Volk * VirtualMailManager/account.py: VMM/account: Account._count_aliases() cast the EmailAddress to str, in order to avoid errors from the database. [ecdba68c7a2c] 2011-01-17 Pascal Volk * VirtualMailManager/handler.py: VMM/handler: Handler.relocated_add() check if the target address' domain is known, if so add a warning when the account/alias doesn't exist. [1016a17684c1] * VirtualMailManager/handler.py: VMM/handler: Handler._get_{account,alias,relocated}() check if the address is already in use. [ef99be5b7ec0] * VirtualMailManager/handler.py: VMM/handler: Handler.alias_add() and Handler.relocated_add() use DestinationEmailAddress instances for target addresses. [3b2fd0b4e51b] 2011-01-17 Tobias Berling * VirtualMailManager/alias.py: VMM/alias.py: Fixed TypeError when calling dbc.executemany() [1b1f8f0cc687] * setup.py: setup.py: Fixed syntax error [4dfc5f3c1f2c] * pgsql/create_tables-dovecot-1.2.x.pgsql, pgsql/create_tables.pgsql: pgsql/create_tables{,-dovecot-1.2.x}.pgsql: Fixed syntax errors [be4bd77dbe57] 2011-01-17 Pascal Volk * VirtualMailManager/emailaddress.py: VMM/emailaddress: Added class DestinationEmailAddress. [a6ad9895989d] 2011-01-14 Pascal Volk * COPYING, VirtualMailManager/__init__.py, VirtualMailManager/account.py, VirtualMailManager/alias.py, VirtualMailManager/aliasdomain.py, VirtualMailManager/cli/__init__.py, VirtualMailManager/cli/config.py, VirtualMailManager/cli/handler.py, VirtualMailManager/cli/main.py, VirtualMailManager/cli/subcommands.py, VirtualMailManager/common.py, VirtualMailManager/config.py, VirtualMailManager/constants.py, VirtualMailManager/domain.py, VirtualMailManager/emailaddress.py, VirtualMailManager/errors.py, VirtualMailManager/ext/__init__.py, VirtualMailManager/ext/postconf.py, VirtualMailManager/handler.py, VirtualMailManager/mailbox.py, VirtualMailManager/maillocation.py, VirtualMailManager/password.py, VirtualMailManager/pycompat/__init__.py, VirtualMailManager/pycompat/hashlib.py, VirtualMailManager/relocated.py, VirtualMailManager/transport.py, setup.py, update_config.py, vmm: Updated copyright notices to include the year 2011. [d6573da35b5f] 2011-01-13 Pascal Volk * INSTALL: merged changes from default(cfd29e65e4f2) [61fdd77d2e65] * INSTALL: INSTALL: Use the %L modifier for %u variable in password_query and user_query. [cfd29e65e4f2] 2010-08-10 Pascal Volk * VirtualMailManager/domain.py: VMM/domain: set_{directory,transport} make sure the domain is new. Assertions could be optimized away. [502d59f4bb34] * VirtualMailManager/cli/subcommands.py, man/de/man1/vmm.1.rst, man/man1/vmm.1.rst, man/substitute_links_1.rst: VMM/cli/subcommands: Added 'auto postmaster' to domain_add(). [c2e8eab2616d] * VirtualMailManager/cli/handler.py, VirtualMailManager/cli/subcommands.py, man/de/man1/vmm.1.rst, man/man1/vmm.1.rst, man/substitute_links_1.rst: VMM/cli/handler: Added support for random passwords in user_add(). [971577b89d26] 2010-08-09 Pascal Volk * VirtualMailManager/transport.py: VMM/transport: Small error message 'improvement'. [44283818f8db] * VirtualMailManager/config.py: VMM/config: Added option mailbox.format to the configuration check. [7fa919dab42c] * update_config.py: update_config: Set pyPgSQL, maybe there is no psycopg2 installed. [6fabb9cd212d] * man/de/man5/vmm.cfg.5.rst, man/man5/vmm.cfg.5.rst: man5/vmm.cfg: Added description for new settings in section database. [095b43faaba5] * INSTALL, setup.py: Updated INSTALL and setup.py. [61e732e4cb3e] * VirtualMailManager/handler.py: VMM/handler: Added support for psycopg2. [48bf20b43f2e] * VirtualMailManager/cli/main.py: VMM/cli/main: Adjusted _get_handler() to changes in config module. [a653c43048b1] * VirtualMailManager/config.py: VMM/config: Extended configuration check and raise only a ConfigError. Added new settings 'module', 'port', and 'sslmode' to the database section. [2ae40cd0d213] 2010-08-07 Pascal Volk * VirtualMailManager/account.py, VirtualMailManager/alias.py, VirtualMailManager/aliasdomain.py, VirtualMailManager/domain.py, VirtualMailManager/maillocation.py, VirtualMailManager/relocated.py, VirtualMailManager/transport.py: VMM/…: Provide parameters as tuple to cursor.execute(). [22d115376e4d] * VirtualMailManager/handler.py: VMM/handler: Small code cleanups. [4bba5fb90b78] * VirtualMailManager/common.py: VMM/common: Reformulated error message in exec_ok. [b7a4d7828608] * VirtualMailManager/cli/config.py: VMM/cli/config: Except the VMMError from exec_ok in configure(). [d60ffbc0124b] * VirtualMailManager/config.py: VMM/config: Use common.VERSION_RE, instead of defining the regexp two times. [ca7575401549] * VirtualMailManager/common.py: VMM/common: Made the version re pattern object accessible as VERSION_RE. Small 'global …' cleanups. [586367ee042b] 2010-08-06 Pascal Volk * VirtualMailManager/cli/handler.py: VMM/cli/handler: Don't perform config and environment checks twice. [95d45e4ec1a6] * VirtualMailManager/cli/subcommands.py, man/de/man1/vmm.1.rst, man/man1/vmm.1.rst: VMM/cli/subcommands: Added subcommands configget and configset. [f8d5c8bb8bce] * VirtualMailManager/cli/config.py: VMM/cli/config: CliConfig.set raise a ConfigError, if the new value could not be set. [0d2430dc6ef8] * VirtualMailManager/cli/main.py: VMM/cli/main: Except more errors and handle them correct. [9232ed7e4d85] * VirtualMailManager/config.py: VMM/config: LazyConfig._get_section_option check for empty section/option names. [b0c971f943dc] 2010-08-05 Pascal Volk * VirtualMailManager/account.py, VirtualMailManager/cli/config.py, VirtualMailManager/cli/handler.py, VirtualMailManager/cli/subcommands.py, VirtualMailManager/emailaddress.py, VirtualMailManager/handler.py, VirtualMailManager/relocated.py, po/vmm.pot: VMM/…: Unified messages and removed non-ASCII quotes. VMM/account: Added TP-comment back. Was accidentally removed in changeset f9a6b6701cf9. Refreshed po/vmm.pot. [6709d0faf2f5] * VirtualMailManager/cli/__init__.py, VirtualMailManager/cli/main.py, VirtualMailManager/cli/subcommands.py, vmm: vmm: Renamed to VirtualMailManager/cli/main.py. Splitted subcommands out to VirtualMailManager/cli/subcommands.py. vmm: New created with minimal code. [4515afec62e5] * VirtualMailManager/cli/handler.py: VMM/cli/handler: CliHandler.user_password: Only prompt for the password if the Account exists. [abff2de9eed0] * VirtualMailManager/account.py: VMM/account: Made Account._services available as account.SERVICES. [45834dcc280e] 2010-08-04 Pascal Volk * VirtualMailManager/cli/handler.py, VirtualMailManager/handler.py: VMM/{,cli/}handler: user_add: Check earlier if the account exists. [150ddcc8b315] 2010-08-03 Pascal Volk * VirtualMailManager/domain.py: VMM/domain: Added missing comma in Domain.get_info's SELECT list. [d24c2ea39710] 2010-08-01 Pascal Volk * VirtualMailManager/handler.py, man/de/man1/vmm.1.rst, man/man1/vmm.1.rst: VMM/handler: Updated Handler.user_{disable,enable} methods to accept a list of services. man/{,de/}man1/vmm.1.rst: Updated userenable/userdisable part. [7a471cace3ba] * VirtualMailManager/account.py: VMM/account: Reworked methods enable/disable in order to accept multiple services. [3f550826b1cc] 2010-07-30 Pascal Volk * VirtualMailManager/account.py, VirtualMailManager/handler.py, man/de/man1/vmm.1.rst, man/man1/vmm.1.rst: VMM/{account,handler}: Account delete, use the boolean keyword force too. [1ed85e696748] * man/de/man1/vmm.1.rst, man/man1/vmm.1.rst: man/{,de/}man1/vmm.1.rst: Updated domaindelete part. [352ca7f1b332] * VirtualMailManager/domain.py, VirtualMailManager/handler.py: VMM/domain: Simplified Domain.delete() related methods and their arguments, VMM/handler: Adjusted to the changes in the domain module. [270b57af85de] * VirtualMailManager/handler.py: VMM/handler: _make_home() Create the domain directory if it doesn't exist for some reason. [3fe8d6cdbe3a] * VirtualMailManager/account.py, VirtualMailManager/handler.py: VMM/account: Replaced property domain_directory by domain. [41789df75339] 2010-07-29 Pascal Volk * VirtualMailManager/common.py: VMM/common: Removed unused import. [85972d3ba936] * VirtualMailManager/handler.py: VMM/handler: Use more common.lisdir() in Handler's methods _chkenv, _delete_domain_dir, _delete_home, _get_disk_usage, _make_domain_dir, and _make_home. Handler._make_home(): Also check if the domain directory exists. [bb9ff81928f1] * VirtualMailManager/common.py, VirtualMailManager/config.py, VirtualMailManager/mailbox.py: VMM/common: Replaced function is_dir() by lisdir(). VMM/{config,mailbox}: Adjusted to the above change. [8f8d9c4c8332] * VirtualMailManager/handler.py: VMM/handler: Reworked methods _get_disk_usage, _delete_home and _delete_domain_dir. Deleted method _isdir. [06c0457036a0] * VirtualMailManager/constants.py: VMM/constants: Added MIN_GID and MIN_UID. [38e344ba3d0f] 2010-07-28 Pascal Volk * VirtualMailManager/mailbox.py: VMM/mailbox: Removed accidentally committed debug print statement. [d58cc465dc61] * VirtualMailManager/alias.py, VirtualMailManager/cli/config.py, VirtualMailManager/config.py, VirtualMailManager/emailaddress.py, VirtualMailManager/mailbox.py, VirtualMailManager/relocated.py: VMM/…: More PEP-8 fixes; eliminated __names. VMM/emailaddress: Fixed™ methods __eq__ and __ne__. (I'm not pylint's nanny.) [94bd10e237e5] * VirtualMailManager/transport.py: VMM/transport: Converted _mixedCase method names to _lower_case_with_underscores. Added missing docstrings. Fixed™ methods __eq__ and __ne__. (I'm not pylint's nanny.) [883d5cd66498] * VirtualMailManager/Account.py, VirtualMailManager/Alias.py, VirtualMailManager/AliasDomain.py, VirtualMailManager/Config.py, VirtualMailManager/Domain.py, VirtualMailManager/EmailAddress.py, VirtualMailManager/Handler.py, VirtualMailManager/Relocated.py, VirtualMailManager/Transport.py, VirtualMailManager/__init__.py, VirtualMailManager/account.py, VirtualMailManager/alias.py, VirtualMailManager/aliasdomain.py, VirtualMailManager/cli/Config.py, VirtualMailManager/cli/Handler.py, VirtualMailManager/cli/__init__.py, VirtualMailManager/cli/config.py, VirtualMailManager/cli/handler.py, VirtualMailManager/common.py, VirtualMailManager/config.py, VirtualMailManager/domain.py, VirtualMailManager/emailaddress.py, VirtualMailManager/errors.py, VirtualMailManager/ext/Postconf.py, VirtualMailManager/ext/postconf.py, VirtualMailManager/handler.py, VirtualMailManager/mailbox.py, VirtualMailManager/maillocation.py, VirtualMailManager/password.py, VirtualMailManager/relocated.py, VirtualMailManager/transport.py: VMM/*: Made all modules names lowercase, adjusted imports. [011066435e6f] * VirtualMailManager/Handler.py, VirtualMailManager/cli/Handler.py: VMM//{,cli/}Handler: PEP-8-ified the Handler classes: * converted CamelCase method names to lower_case_with_underscores * eliminated __names * added missing docstrings [f4956b4ceba1] 2010-07-27 Pascal Volk * VirtualMailManager/Handler.py, VirtualMailManager/cli/Handler.py: VMM//{,cli/}Handler: Reworked configuration related parts. Renamed attributes _Cfg -> _cfg; _cfgFileName -> _cfg_fname. Renamed methods __chkCfgFile -> __check_cfg_file; __findCfgFile -> __find_cfg_file. Added missing docstrings. [4dc2edf02d11] * VirtualMailManager/constants.py: VMM/constants: Added __copyright__. [d619e97a8f18] 2010-07-26 Pascal Volk * VirtualMailManager/Account.py, VirtualMailManager/Alias.py, VirtualMailManager/AliasDomain.py, VirtualMailManager/Config.py, VirtualMailManager/Domain.py, VirtualMailManager/EmailAddress.py, VirtualMailManager/Handler.py, VirtualMailManager/Relocated.py, VirtualMailManager/Transport.py, VirtualMailManager/__init__.py, VirtualMailManager/cli/Config.py, VirtualMailManager/cli/Handler.py, VirtualMailManager/cli/__init__.py, VirtualMailManager/common.py, VirtualMailManager/constants.py, VirtualMailManager/constants/ERROR.py, VirtualMailManager/constants/EXIT.py, VirtualMailManager/constants/__init__.py, VirtualMailManager/constants/version.py, VirtualMailManager/ext/Postconf.py, VirtualMailManager/mailbox.py, VirtualMailManager/maillocation.py, VirtualMailManager/password.py: VMM/constants: Replaced the constants subpackage by a module. [31d8931dc535] * VirtualMailManager/cli/__init__.py: VMM/cli: Forgot to remove string_io from __all__. [81bccfd14355] * VirtualMailManager/cli/__init__.py: VMM/cli: Removed misplaced function string_io. [0b4a6e9d9f27] * VirtualMailManager/cli/Config.py: VMM/cli/Config: Write warnings to stderr. Renamed method __saveChanges to __save_changes. [c17c46d9e440] * VirtualMailManager/cli/__init__.py: VMM/cli: w_err() call os.sys.exit only if code != 0. read_pass() write errors to stderr. And a few other small fixes. [6f39a1e56f4a] 2010-07-25 Pascal Volk * VirtualMailManager/ext/Postconf.py: VMM/ext/Postconf: Reworked class Postconf. Added method edit(). [a0a10100aee5] 2010-07-23 Pascal Volk * VirtualMailManager/Handler.py: VMM/Handler: Reworked __domDirMake and reamed to __make_domain_dir. Removed the (now) unused method __makedir. [644e2cc4a441] * VirtualMailManager/Handler.py, VirtualMailManager/mailbox.py: VMM/mailbox: Added to the repository. VMM/Handler: Integrated mailbox module. Code cleanups. [d21423478803] * VirtualMailManager/Transport.py: VMM/Transport: Small cosmetics. [aa4a9fc31e1b] * VirtualMailManager/maillocation.py, man/de/man5/vmm.cfg.5.rst, man/man5/vmm.cfg.5.rst, vmm.cfg: VMM/maillocation: Dovecot >= 2.0.beta5 is required for `doveadm mailbox create -s …` [217b419d6561] 2010-07-22 Pascal Volk * VirtualMailManager/Account.py: VMM/Account: s/prefix/mbformat/ and pass the dbh to MailLocation.__init__. [504fd29b4712] * VirtualMailManager/maillocation.py: VMM/maillocation: Renamed MailLocation's property prefix to mbformat. [3c62f581d17a] * VirtualMailManager/Config.py, man/de/man5/vmm.cfg.5.rst, man/man5/vmm.cfg.5.rst, man/substitute_links_5.rst, vmm.cfg: VMM/Config: Added boolean option mailbox.subscribe. [df0f7b22540c] * VirtualMailManager/Config.py: VMM/Config: Return mailbox.{folders,root} settings as Unicode. [8dd3a107fd92] 2010-07-21 Pascal Volk * VirtualMailManager/Account.py, VirtualMailManager/maillocation.py: VMM/maillocation: Reworked class MailLocation to match the new database structure. [32b4a39b5640] * VirtualMailManager/Config.py: VMM/Config: Added function check_mailbox_format(). [e1d3f027dd64] 2010-07-20 Pascal Volk * man/de/man5/vmm.cfg.5.rst: man/de/man5/vmm.cfg.5.rst: Fixed a typo. [efa001edc349] * man/de/man5/vmm.cfg.5.rst, man/man5/vmm.cfg.5.rst: man/{,de/}man5/vmm.cfg.5.rst: Added mailbox.root, small updates. [d15a27eaa9d2] * VirtualMailManager/Config.py, update_config.py, vmm.cfg: VMM/Config: Added mailbox.root setting. [ee89399346cb] * TODO, pgsql/create_optional_types_and_functions-dovecot-1.2.x.pgsql, pgsql/create_optional_types_and_functions.pgsql, pgsql /create_tables-dovecot-1.2.x.pgsql, pgsql/create_tables.pgsql, pgsql/update_tables_0.5.x-0.6-dovecot-1.2.x.pgsql, pgsql/update_tables_0.5.x-0.6.pgsql, pgsql/update_types_and_functions_0.5.x-0.6-dovecot-1.2.x.pgsql, pgsql/update_types_and_functions_0.5.x-0.6.pgsql: pgsql: Added support for different mailbox formats. - users.passwd can store sha512-crypt.hex hashes now - Added new update scripts. [e21ceaabe871] 2010-07-12 Pascal Volk * pgsql/update_tables_0.4.x-0.5.pgsql, pgsql/update_tables_0.5.x_for_dovecot-1.2.x.pgsql, pgsql/update_types_and_functions_0.5.x_for_dovecot-1.2.x.pgsql: pgsql: Removed old update SQL scripts. [62211b6a9b8e] 2010-07-04 Pascal Volk * VirtualMailManager/Alias.py, VirtualMailManager/maillocation.py: VMM/maillocation: Code cleanups. Fixed error introduced with changeset 084300a00ee1. [18086c6a2521] 2010-05-18 Pascal Volk * VirtualMailManager/Domain.py, VirtualMailManager/Handler.py: VMM/Domain: removed functions ace2idna() and idn2ascii(). domainname.encode('idna')/domainname.decode('idna') works too. [7d1bafc6fa30] 2010-05-12 Pascal Volk * VirtualMailManager/Account.py, VirtualMailManager/Handler.py: VMM/Account: replaced attribute _mid : int by _mail : MailLocation. [a77b67673aa6] 2010-05-11 Pascal Volk * TODO, VirtualMailManager/password.py: VMM/password: added some CRYPT_* constants. Reverted modification (284:ec1966828246) in _get_salt(). [619dadc0fd25] 2010-05-10 Pascal Volk * VirtualMailManager/Config.py, VirtualMailManager/password.py, man/de/man5/vmm.cfg.5.rst, man/man5/vmm.cfg.5.rst: VMM/password: adapted Blowfish/SHA-256/SHA-512 crypt() to recent changes in the Dovecot-2.0 source tree. VMM/Config: Added default number of encryption rounds to the configuration. man5: updated description of misc.crypt_{blowfish,sha{256,512}}_rounds. [7ef3f117a230] 2010-05-09 Pascal Volk * VirtualMailManager/Account.py, VirtualMailManager/Alias.py, VirtualMailManager/AliasDomain.py, VirtualMailManager/Config.py, VirtualMailManager/Domain.py, VirtualMailManager/EmailAddress.py, VirtualMailManager/Handler.py, VirtualMailManager/Relocated.py, VirtualMailManager/cli/Config.py, VirtualMailManager/ext/Postconf.py, VirtualMailManager/password.py: VMM/…: re-indented long queries and error messages. [e2785e04f92e] 2010-05-05 Pascal Volk * VirtualMailManager/password.py: VMM/password: generate all crypt() salts w/o trailing $ sign [142f188f7552] 2010-05-04 Pascal Volk * man/de/man5/vmm.cfg.5.rst, man/man5/vmm.cfg.5.rst: man5: added misc.dovecot_version to the minimal config example. [01cb71c1ae33] * VirtualMailManager/Config.py, VirtualMailManager/password.py: VMM/password: moved the 'scheme check' code from pwhash() to the new function verify_scheme(). VMM/Config: use verify_scheme() to check the scheme when LazyConfig.set() is called. [1e77dd639fa3] 2010-05-03 Pascal Volk * VirtualMailManager/Config.py, man/de/man5/vmm.cfg.5.rst, man/man5/vmm.cfg.5.rst: VMM/Config: removed the default value of misc.dovecot_version. There are too many different versions installed on different systems. So, it doesn't make much sense to have a default value. [e2046d47688b] * VirtualMailManager/Handler.py: VMM/Handler: import the errors before raising them. Rephrased the 'permission error' message. [d30a94f5aef5] * TODO, VirtualMailManager/Config.py, VirtualMailManager/password.py, man/de/man5/vmm.cfg.5.rst, man/man5/vmm.cfg.5.rst, man/substitute_links_5.rst: VMM/password: Added support Blowfish/SHA-256/SHA-512 crypt(). Also updated Config and man section 5. [ec1966828246] 2010-04-30 Pascal Volk * VirtualMailManager/Handler.py: VMM/Handler: code cleanups in the user/alias/relocated info methods. [ea6d052de24a] * VirtualMailManager/Handler.py: VMM/Handler: fixed destination check in Handler.aliasAdd(). [28871c1be260] * VirtualMailManager/Alias.py, VirtualMailManager/Handler.py, VirtualMailManager/cli/Handler.py: VMM/Alias: moved the postconf stuff from the Handlers to Alias class. [59ff7c719697] * VirtualMailManager/Config.py, VirtualMailManager/Handler.py: VMM/{Config,Handler}: moved Config.install() to Handler.cfg_install(). Handler.cfg_install() must be called explicitly. To avoid 'strange' problems. [db35d2eec518] 2010-04-29 Pascal Volk * VirtualMailManager/cli/Handler.py: VMM/cli/Handler: fixed AttributeError in CliHandler.__init__(). Attribute '_scheme' was removed in previous commit. [74d94b867348] * VirtualMailManager/Handler.py: VMM/Handler: removed password hashing related stuff. [5d229a50b115] * VirtualMailManager/Handler.py, VirtualMailManager/cli/Handler.py: VMM{,/cli}/Handler: adjusted user* methods to the changes in Account. All user methods was renamed from userAction() to user_action(). Added missing docstrings. [e50ffc0b8468] * VirtualMailManager/Account.py: VMM/Account: always pass the EmailAddress to the pwhash() call. [f2ecfe0a0e09] * VirtualMailManager/Account.py, VirtualMailManager/constants/ERROR.py: VMM/Account: some modifications and small improvements in class Account. - replaced the tid by a Transport instance - check mailbox format dependencies in _repare() - reset all attributes when the Account was deleted - don't select information, we have already, from the db - added __nonzero__() method [524f7ed5ad5b] * VirtualMailManager/password.py: VMM/password: added small output check on _dovecotpw(). [45ec5c3cfef4] * VirtualMailManager/common.py: VMM/common: added a caching dict for version_hex()/version_str() [77fc7138ef6a] 2010-04-28 Pascal Volk * VirtualMailManager/Config.py, VirtualMailManager/Handler.py, VirtualMailManager/__init__.py, VirtualMailManager/password.py: VMM/Config: Added method Config.install() -> global cfg_dget(). VirtualMailManager.Configuration removed -> some adjustments. [446483386914] * INSTALL, UPGRADE: INSTALL: dropped group mail related stuff. UPGRADE: be more details about nobody's primary group. [e915d4725706] * VirtualMailManager/Config.py, VirtualMailManager/Handler.py, man/de/man5/vmm.cfg.5.rst, man/man5/vmm.cfg.5.rst, update_config.py, vmm.cfg: configuration: Dropped setting misc.gid_mail. That setting was never useful, since none of the virtual users was a member of a system group. [d3389645a91d] * man/de/man5/vmm.cfg.5.rst, man/man5/vmm.cfg.5.rst, man/substitute_links.rst: man/{,de/}man5/vmm.cfg.5.rst: Updated descriptions of dovecotpw and password_scheme settings. [188ea8d6072f] * INSTALL, VirtualMailManager/password.py, VirtualMailManager/pycompat/hashlib.py: VMM: added new modules password and pycompat.hashlib. INSTALL: updated [beb8f4421f92] * VirtualMailManager/maillocation.py: VMM/maillocation: Use the hex version, since we are able to convert it to an string, in case of a failure. [084300a00ee1] 2010-04-27 Tobias Berling * VirtualMailManager/Account.py, VirtualMailManager/Handler.py, VirtualMailManager/common.py: VMM/{Account,common,Handler}: Improved version_hex(). - common: version_hex() now supports 'serials' > 16. Added version_str() as counterpart to version_hex(). - Account, Handler: updated hardcoded Dovecot versions. [e14c345b44a1] 2010-04-26 Pascal Volk * VirtualMailManager/Account.py, VirtualMailManager/common.py, VirtualMailManager/maillocation.py: VMM/{Account,common,maillocation}: Dovecot version (check) fixes. - Account: fixed versions dependencies. - maillocation: use the version string, may be uses in a error message. - common: version_hex() raises a ValueError, instead of returning 0, if the version string is invalid. [3c0173418d5d] 2010-04-25 Pascal Volk * VirtualMailManager/Account.py, VirtualMailManager/Config.py, VirtualMailManager/Handler.py, VirtualMailManager/maillocation.py, man/de/man5/vmm.cfg.5.rst, man/man5/vmm.cfg.5.rst, update_config.py, upgrade.sh, vmm.cfg: Use the complete Dovecot version, not only the concatenated major and minor parts. (1.2.11 instead of 12). [04fea4d8b900] * VirtualMailManager/common.py: VMM/common: improved version_hex() in order to convert also alpha, beta and release candidate versions. [07fdc93dde9f] 2010-04-24 Pascal Volk * VirtualMailManager/Config.py, VirtualMailManager/Handler.py, VirtualMailManager/__init__.py, VirtualMailManager/common.py: VMM: moved some non-init functions to the new common module. Adjusted imports in the Config and Handler module. [6eea85d8b91d] 2010-04-23 Pascal Volk * setup.py: setup.py: Added sub-package pycompat to the packages list. [1c2241dde942] 2010-04-22 Pascal Volk * VirtualMailManager/pycompat.py, VirtualMailManager/pycompat/__init__.py: VMM/pycompat: is now a sub-package. [b052a2f0f5d4] 2010-04-20 Pascal Volk * VirtualMailManager/Handler.py: merged changes from default(9bf8d97ced88) [6c699837b4d4] * VirtualMailManager/VirtualMailManager.py: VMM/VMM: corrected name of password scheme PLAIN-MD4. [9bf8d97ced88] 2010-04-18 Tobias Berling * VirtualMailManager/Alias.py, VirtualMailManager/AliasDomain.py, pgsql/create_tables-dovecot-1.2.x.pgsql, pgsql/create_tables.pgsql: VMM/Alias.py: Replaced some %r with '%s'. VMM/AliasDomain.py: save(), switch(), delete(): Update AliasDomain._gid after database change. Added dbc.close() to AliasDomain.delete(). create_tables{,-dovecot-1.2.x}.pgsql: Fixed a typo. [5b8fde01e4f0] 2010-04-18 Pascal Volk * TODO, VirtualMailManager/Handler.py, VirtualMailManager/__init__.py: VMM: added 'Configuration' variable and set_configuration(). Handler.__init__() now exports its config via set_configuration(). [ae80282301a3] 2010-04-17 Pascal Volk * VirtualMailManager/Handler.py: VMM/Handler: renamed some methods and added their missing docstrings: - cfgDget() -> cfg_dget() - cfgPget() -> cfg_pget() - userByID() -> user_by_uid() [d2ddd4a6528d] 2010-04-16 Pascal Volk * VirtualMailManager/AliasDomain.py, VirtualMailManager/Domain.py, VirtualMailManager/EmailAddress.py, VirtualMailManager/Handler.py, VirtualMailManager/__init__.py: VMM: moved functions ace2idna(), check_domainname(), idn2ascii() and relevant parts to the Domain module. Adjusted imports in modules AliasDomain, EmailAddress and Handler. [8aecc83a0d32] * VirtualMailManager/EmailAddress.py, VirtualMailManager/__init__.py: VMM: moved check_localpart() to the EmailAddress module. [58d1b6f41664] 2010-04-15 Pascal Volk * VirtualMailManager/Account.py: VMM/Account: renamed function getAccountByID -> get_account_by_uid. Fixed AttributeError (tid) in Account._prepare(). Removed unused imports. [af555e6967c8] * VirtualMailManager/Handler.py: VMM/Handler: Implemented Handler._chk_other_address_types(). Removed the static methods accountExists(), aliasExists(), relocatedExists() and _exists(). [0963ad2f5fe2] * VirtualMailManager/Account.py, VirtualMailManager/Alias.py, VirtualMailManager/Relocated.py: VMM/{Account,Alias,Relocated}: added a address property to the classes Account, Alias and Relocated. [73cd082cd724] * VirtualMailManager/Relocated.py: VMM/Relocated: implemented Relocated.__nonzero__() for truth value testing. Use EmailAddress' __str__() method when raising RelocatedErrors. [bb7d9906c529] * INSTALL: merged changes from default(dbcb29ac89fa) [da241eacad4d] * INSTALL: INSTALL: simplified the 'SETUID copy of deliver' part [dbcb29ac89fa] * VirtualMailManager/Account.py: VMM/Account: Account.get_info() use the domain's transport, if tid matches. [481280686789] * VirtualMailManager/Account.py, VirtualMailManager/constants/ERROR.py: VMM/Account: reworked class Account. [f9a6b6701cf9] 2010-04-13 Pascal Volk * VirtualMailManager/Handler.py: VMM/Handler: adjusted Handler.aliasDomain*() methods to changes in the AliasDomain class. Updated docstrings. [698ba4208ddc] * VirtualMailManager/AliasDomain.py: VMM/AliasDomain: some cleanups in class AliasDomain. Added missing docstrings. [30aaf2bc079c] 2010-04-05 Pascal Volk * VirtualMailManager/Domain.py: VMM/Domain: search() small code cleanups. [496099847480] * VirtualMailManager/Handler.py: VMM/Handler: small code cleanups and an improved import statement. [2493453f2c10] * VirtualMailManager/Domain.py, VirtualMailManager/Handler.py: merged changes from default(184970fd7486) [fb6336b25b8f] * VirtualMailManager/Domain.py: VMM/Domain: search() lists now all matching domains, also when the result contains primary and alias names, but the found alias is not an alias for any of the found primaries. [184970fd7486] * VirtualMailManager/VirtualMailManager.py: VMM/VMM: applied 'code compression' from v0.6.x(09b7e3fe29b3) to VirtualMailManager.domainList(). [8f56166a3283] 2010-04-04 Tobias Berling * VirtualMailManager/Handler.py: VMM/Handler: compressed Handler.domainList() and replaced “%s” with '%s' in error message. [09b7e3fe29b3] 2010-04-04 Pascal Volk * VirtualMailManager/Account.py, VirtualMailManager/AliasDomain.py, VirtualMailManager/Domain.py, VirtualMailManager/Handler.py: VMM/Domain: reworked Domain class. Adjusted classes Account, AliasDomain and Handler to changes in the Domain class. [084331dd1e4c] 2010-04-03 Pascal Volk * VirtualMailManager/Account.py, VirtualMailManager/Transport.py: VMM/Transport: Renamed attribute/property {,_}id to {,_}tid. [9d3405ed08e5] 2010-04-02 Pascal Volk * VirtualMailManager/__init__.py: VMM: check_localpart() use '%s' instead of %r in error messages. [e88ba0fb1281] 2010-03-21 Pascal Volk * VirtualMailManager/Handler.py: merged changes from default(8c4df3dd2d2c) [55503d63ba30] * vmm: vmm: minimal cleanups. [3c766114d0b9] * VirtualMailManager/VirtualMailManager.py: VMM: VirtualMailManager.__pwhash() added support for 'doveadm pw'. dovecotpw was replaced by `doveadm pw` in Dovecot v2.0. To use doveadm instead of dovecotpw use the following settings in vmm.cfg: [bin] dovecotpw = /path/to/doveadm [misc] dovecotvers = 20 [8c4df3dd2d2c] 2010-03-03 Pascal Volk * man/de/man5/vmm.cfg.5.rst, man/man5/vmm.cfg.5.rst, man/substitute_links_5.rst: man: updated configuration manual pages (maildir.* -> mailbox.*). [eefbe052a135] * VirtualMailManager/Config.py, update_config.py, vmm.cfg: vmm.cfg: renamed maildir.folders to mailbox.folders. maildir.name was removed. new: mailbox.format, in order to support all mailbox formats from Dovecot. Maildir is the default format. Adjusted VirtualMailManager/Config and update_config.py to the changes mentioned above. [0fb2f12648a7] * VirtualMailManager/Account.py, VirtualMailManager/maillocation.py: VMM/maillocation: MailLocation.__init__(): take a 'format' name, instead of a 'directory' name. - added function known_format() to the module. VMM/Account: Adjusted to above changes. [a7b000ca4ac9] 2010-03-02 Pascal Volk * VirtualMailManager/Account.py, VirtualMailManager/constants/ERROR.py, VirtualMailManager/maillocation.py, doc/source/vmm_constants_error.rst: VMM/Account: Adjusted to changes in maillocation.MailLocation. VMM/maillocation: Class MailLocation added missing property `mid`. Added new error to VMM/constants/ERROR and doc/source/vmm_constants_error [87db9f1f95ea] * VirtualMailManager/MailLocation.py, VirtualMailManager/maillocation.py, doc/source/vmm_constants_error.rst: VMM/maillocation: rewrote MailLocation class. Renamed MailLocation.py to maillocation.py. [311eee429f67] 2010-03-01 Pascal Volk * VirtualMailManager/Account.py, VirtualMailManager/Domain.py, VirtualMailManager/Transport.py: VMM/Transport: reworked Transport class. Use assertions for argument checks. Removed methods getID() and getTransport(). This values are now accessible through the read-only attributes id and transport. VMM/{Account,Domain}: adjusted to modifications in Transport class. A few code cleanups. [a51809f7940b] * VirtualMailManager/pycompat.py: VMM/pycompat: added function any() for Python 2.4 [7e9874a50d92] * VirtualMailManager/Alias.py, VirtualMailManager/Handler.py, VirtualMailManager/ext/Postconf.py: replaced some "for x in list …" [5c7b7cbb01cd] 2010-02-28 Pascal Volk * VirtualMailManager/Alias.py, VirtualMailManager/Domain.py, VirtualMailManager/Handler.py, VirtualMailManager/Relocated.py: VMM/Domain: get_gid() return 0 instead of raising an Exception, if the domain wasn't found in the database. [d0c16e70a9fb] 2010-02-27 Pascal Volk * VirtualMailManager/Alias.py, VirtualMailManager/Handler.py: VMM/Alias: renamed Alias.add_destination() to add_destinations(). Now it's possible to add one ore more destinations to the alias with a single method call. VMM/Handler: adjusted Handler.aliasAdd() to the API changes of the Alias class. Also use get_gid from the Domain module to get the gid of a domain. We don't need complete Domain object, only the gid. Handler.getWarnings(): no longer return the __warnings list. Return a copy instead and empty the Handler.__warnings list. [371ae0b4443d] * doc/source/index.rst, doc/source/vmm_alias.rst, doc/source/vmm_constants_error.rst, doc/source/vmm_relocated.rst: doc: Added the alias documentation. [8b8d632f0ef3] * VirtualMailManager/pycompat.py: VMM/pycompat: added to the repository. Provides all() for Py24. [0b6ce895e1dc] * VirtualMailManager/EmailAddress.py: VMM/EmailAddress: implemented EmailAddress.__hash__(). So we can have a set() of EmailAddress instances. [84094c7fa28b] * VirtualMailManager/cli/__init__.py: VMM/cli: small optimizations in the functions w_std() and w_err() [eecd05e31517] 2010-02-26 Pascal Volk * VirtualMailManager/Account.py, VirtualMailManager/Alias.py, VirtualMailManager/AliasDomain.py, VirtualMailManager/Config.py, VirtualMailManager/Domain.py, VirtualMailManager/EmailAddress.py, VirtualMailManager/Exceptions.py, VirtualMailManager/Handler.py, VirtualMailManager/MailLocation.py, VirtualMailManager/Relocated.py, VirtualMailManager/Transport.py, VirtualMailManager/__init__.py, VirtualMailManager/cli/Config.py, VirtualMailManager/cli/Handler.py, VirtualMailManager/errors.py, VirtualMailManager/ext/Postconf.py, doc/source/index.rst, doc/source/vmm.rst, doc/source/vmm_config.rst, doc/source/vmm_constants_error.rst, doc/source/vmm_emailaddress.rst, doc/source/vmm_errors.rst, doc/source/vmm_exceptions.rst, doc/source/vmm_relocated.rst: Moved VirtualMailManager/Exceptions to VirtualMailManager/errors. Renamed VMM*Exception classes to *Error. No longer add the attribute 'message' to VMMError if it doesn't exist, like in Python 2.4. It has been deprecated as of Python 2.6. Also removed the methods code() and msg(), the values are now accessible via the attributes 'code' and 'msg'. [0c8c053b451c] 2010-02-25 Pascal Volk * VirtualMailManager/Alias.py, VirtualMailManager/Config.py, VirtualMailManager/EmailAddress.py, VirtualMailManager/Handler.py, VirtualMailManager/Relocated.py, VirtualMailManager/__init__.py, VirtualMailManager/cli/Config.py, VirtualMailManager/cli/__init__.py, VirtualMailManager/constants/VERSION.py, VirtualMailManager/constants/version.py, doc/source/vmm.rst, doc/source/vmm_config.rst, doc/source/vmm_relocated.rst: PEP-8-ified the work of the last days. Renamed methods in class Alias: addDestination() -> add_destination() delDestination() -> del_destination() getDestinations() -> get_destinations() Renamed methods in class Relocated: setDestination() -> set_destination() getInfo() -> get_info() Renamed VMM/constants/VERSION.py -> VMM/constants/version.py Adjusted relevant parts of the documentation. [33f727efa7c4] 2010-02-24 Pascal Volk * VirtualMailManager/Exceptions.py: VMM/Exceptions: use the inherited CTor in all VMM*Exception classes. [84e6e898e6c5] * VirtualMailManager/Alias.py, VirtualMailManager/EmailAddress.py, VirtualMailManager/Relocated.py: VMM:/{Alias,EmailAddress,Relocated}: use assertions for argument checks. [1a9fee6b93bc] * doc/source/conf.py, doc/source/index.rst, doc/source/vmm.rst, doc/source/vmm_constants_error.rst, doc/source/vmm_emailaddress.rst, doc/source/vmm_exceptions.rst, doc/source/vmm_relocated.rst: doc: extended documentation. [77ac6f572855] 2010-02-22 Pascal Volk * VirtualMailManager/Config.py: VMM/Config: LazyConfig.getboolean(), convert the value in our getboolean. So RawConfigParser hasn't to do the whole get() stuff again. [0b129678cfe1] * .hgignore: Added 'doc/build' to the ignored files. [3ddb735ec9d2] * VirtualMailManager/Config.py, VirtualMailManager/__init__.py, doc/Makefile, doc/source/conf.py, doc/source/index.rst, doc/source/vmm.rst, doc/source/vmm_config.rst: VMM{/Config}: reduced docstrings. Added doc to the repository. [c705a9e38962] * VirtualMailManager/EmailAddress.py, VirtualMailManager/Handler.py, VirtualMailManager/__init__.py: VMM: removed unneeded/duplicated regular expression definitions [efa1327b721f] * VirtualMailManager/Config.py: VMM/Config: moved Config.sections() to class LazyConfig. No longer import the ENCODING from VirtualMailManager, it's no longer required in the Config module. [95be8f62bc0c] 2010-02-20 Pascal Volk * VirtualMailManager/Config.py, VirtualMailManager/cli/Config.py: VMM/Config: renamed LazyConfig's get_boolean() to getboolean(). VMM/cli/Config: import required ConfigParser.RawConfigParser. [da07dd944ad1] 2010-02-13 Pascal Volk * VirtualMailManager/cli/__init__.py: VMM/cli: added __init__.py to the repository. [bc9726c9ad85] * VirtualMailManager/Config.py, VirtualMailManager/cli/Config.py: VMM/{,cli/}Config: fixed imports. Small code cleanups and cosmetic. [83938336c518] 2010-02-12 Pascal Volk * VirtualMailManager/Alias.py: VMM/Alias: small code cleanups and cosmetic. [4d601240b7db] * VirtualMailManager/Relocated.py: VMM/Relocated: small code cleanups and cosmetic. [43e7c8b440da] * VirtualMailManager/Config.py: VMM/Config: LazyConfigOption.__init__() cast 'default' to 'cls'. If the default value is not None, make sure it has the proper type. [dbb0f7ed7858] 2010-02-11 Pascal Volk * VirtualMailManager/Config.py: VMM/Config: attributes of class LazyConfigOption are read-only now. Some small code cleanups. [983cf98d5881] * VirtualMailManager/AliasDomain.py, VirtualMailManager/Domain.py, VirtualMailManager/EmailAddress.py, VirtualMailManager/__init__.py: VMM: renamed function chk_domainname() -> check_domainname(). Moved EmailAddress.check_localpart() -> VirtualMailManager.check_localpart(). Some small code cleanups in class EmailAddress. [0684790fff7c] 2010-02-10 Pascal Volk * VirtualMailManager/Alias.py, VirtualMailManager/Domain.py, VirtualMailManager/Relocated.py: VMM/Domain: added function get_gid() to the Domain module. We don't need to load all the domain related information from the database, when we need only the GID of a domain. For example in the Alias or Relocated classes. [02d467e4fbab] * VirtualMailManager/Handler.py, VirtualMailManager/Relocated.py: VMM/{Relocated,Handler}: reworked Relocated class, adjusted Handler. [d2712e8c724e] * VirtualMailManager/Alias.py, VirtualMailManager/Handler.py: VMM/{Alias,Handler}: reworked Alias class, adjusted Handler class. Handler: - attribute _dbh is no longer private, the VMM/cli/Handler uses it also. - adjusted to changes in Alias and EmailAddress classes. [65a3163bd113] 2010-02-09 Pascal Volk * VirtualMailManager/EmailAddress.py: VMM/EmailAddress: reworked class EmailAddress again. The attributes domainname and localpart are now read-only. [05dd49fc3ea1] * VirtualMailManager/EmailAddress.py: VMM/EmailAddress: reworked once more. - moved EmailAddress.__chkLocalpart() -> __module__.check_localpart() - renamed EmailAddress.__chkAddress() -> EmailAddress._chk_address() - attributes domainname and localpart are no longer protected - added missing doc strings. [6c06edb5b2d2] 2010-02-08 Pascal Volk * VirtualMailManager/EmailAddress.py: VMM/EmailAddress: rework EmailAddress class. [a259bdeaab5c] 2010-02-07 Pascal Volk * VirtualMailManager/Handler.py: VMM/Handler: fixed a SyntaxError. Oops [0854fb9f3bc5] * VirtualMailManager/Handler.py: VMM/Handler: __mailDirMake() add warning for skipped mailboxes. - domainInfo() removed old deprecated warning. - fixed PEP8 warnings. [db77501aeaed] * VirtualMailManager/Handler.py, VirtualMailManager/cli/Handler.py: VMM/{,cli/}Handler: reverted most of cs cf1b5f22dbd2 added a cli handler. Moved the interactive stuff from VMM/Handler to the derived VMM/cli/Handler. [1903d4ce97d7] 2010-02-06 Pascal Volk * VirtualMailManager/cli/CliConfig.py, VirtualMailManager/cli/Config.py: moved VMM/cli/CliConfig to VMM/cli/Config [e63853509ad0] * VirtualMailManager/Config.py, VirtualMailManager/Handler.py, VirtualMailManager/__init__.py, vmm: VMM/Handler: __init__ accepts now a config_type ('default'||'cli'). - fixed syntax errors, introduced with the last commit. VMM/Config: added Config.configure() -> NotImplementedError. VMM/__init__: install gettext global, everything depends on it. [cf1b5f22dbd2] * VirtualMailManager/Config.py, VirtualMailManager/Handler.py, VirtualMailManager/__init__.py, VirtualMailManager/cli/CliConfig.py: VMM/{,cli/Cli}Config: Moved interactive stuff to new CliConfig class. Renamed Config.getsections() to Config.sections(). Small cosmetics. [38b9a9859749] 2010-02-05 Pascal Volk * VirtualMailManager/Handler.py, VirtualMailManager/cli/handler.py: moved VMM/cli/handler to VMM/Handler [18757fd45e60] * VirtualMailManager/Account.py, VirtualMailManager/Alias.py, VirtualMailManager/AliasDomain.py, VirtualMailManager/Config.py, VirtualMailManager/Domain.py, VirtualMailManager/EmailAddress.py, VirtualMailManager/MailLocation.py, VirtualMailManager/Relocated.py, VirtualMailManager/Transport.py, VirtualMailManager/__init__.py, VirtualMailManager/cli/handler.py, VirtualMailManager/ext/Postconf.py, vmm: VMM/*: Moved some methods from classes to modules __init__. - Adjusted many import statements. - Small adjustments and whitespace cosmetics in Config.py [6e1ef32fbd82] 2010-02-04 Pascal Volk * VirtualMailManager/VirtualMailManager.py, VirtualMailManager/cli/handler.py: moved VMM/VMM to VMM/cli/handler [d0425225ce52] 2010-02-02 Pascal Volk * vmm: vmm: reworked subcommand/arguments mapping. [eb4c73d9d0a4] 2010-02-01 Pascal Volk * VirtualMailManager/VirtualMailManager.py: VMM/VMM: Allow version/help subcommands even with missing configuration options. [84811fcc3c69] * update_config.py: update_config: do not add options w/ default values. remove config.done [866a6d679fce] * VirtualMailManager/Config.py, VirtualMailManager/VirtualMailManager.py, man/de/man5/vmm.cfg.5.rst, man/man5/vmm.cfg.5.rst, vmm: VMM/Config: dropped option config.done [f8279c90e99c] 2010-01-30 Pascal Volk * man/de/man1/vmm.1.rst, man/de/man5/vmm.cfg.5.rst, man/man1/vmm.1.rst, man/man5/vmm.cfg.5.rst, man/substitute_links.rst, man/substitute_links_1.rst, man/substitute_links_5.rst, po/de.po: man: updated documentation … [3d09c657e9e5] 2010-01-29 Pascal Volk * VirtualMailManager/Config.py, po/de.po, po/vmm.pot: po: Quick refresh - for documentation purpose. [9480f2b15129] 2010-01-26 Pascal Volk * man/de/man5/vmm.cfg.5.rst, man/man5/vmm.cfg.5.rst: man/{,de/}man5: added a note about how to use default settings. man/de/man5: s/Abschnitt*/Sektion*/g [ec2e1df8bb10] 2010-01-25 Pascal Volk * man/de/man5/vmm.cfg.5.rst, man/man5/vmm.cfg.5.rst, vmm.cfg: man: Added used default values to manual. [cc0d79842fdf] 2010-01-24 Pascal Volk * VirtualMailManager/Config.py, VirtualMailManager/VirtualMailManager.py, vmm: VMM/VMM: allow configure subcommand also with missing settings. Removed method VirtualMailManager.setupIsDone() VMM/Config: Config.load() added 'finally' clause. [b241272eb1bd] * VirtualMailManager/Config.py, VirtualMailManager/VirtualMailManager.py, VirtualMailManager/__init__.py, vmm: VMM/Config: reworked configuration handling. Implemented LazyConfig(RawConfigParser) and LazyConfigOption(object) Rewrote Config class: * use default values and added some validation stuff * removed attributes: __VMMsections and __changes * replaced methods __chkSections() and __chkOptions() with __chkCfg VMM/VMM: Adjusted to reworked Config class. * removed attribute __cfgSections * removed methods: cfgGetBoolean(), cfgGetInt(), cfgGetString() * added methods: cfgDget(), cfgPget(), cfgSet() VMM/__init__: added function get_unicode() vmm: Adjusted to replaced methods in VMM/VMM. [974bafa59330] 2010-01-22 Pascal Volk * VirtualMailManager/Config.py: branch merge [c0e2c7687dd3] * VirtualMailManager/Config.py: VMM/Config: Fixed error handling of missing sections in vmm.cfg. [6526072ec709] * VirtualMailManager/Config.py: VMM/Config: Fixed error handling of missing sections in vmm.cfg. [6f8ac86d1611] 2010-01-18 Pascal Volk * TODO, man/de/man5/vmm.cfg.5, man/de/man5/vmm.cfg.5.rst, man/man5/vmm.cfg.5, man/man5/vmm.cfg.5.rst: man: reworded some parts. [d9ca5c46c1fa] * man/de/man5/vmm.cfg.5, man/de/man5/vmm.cfg.5.rst, man/man5/vmm.cfg.5, man/man5/vmm.cfg.5.rst: man: updated man/{de/,}man5/vmm.cfg.5, using reStructuredText now [a72908248153] 2010-01-14 Pascal Volk * VirtualMailManager/Config.py, VirtualMailManager/VirtualMailManager.py, update_config.py, vmm, vmm.cfg: vmm.cfg: a few re-renamed configuration options. old new -------------------------------------------------- account.password_len -> account.password_length domain.force_del -> domain.force_deletion misc.base_dir -> misc.base_directory misc.dovecot_vers -> misc.dovecot_version [fd496561acc6] 2010-01-13 Pascal Volk * install.sh, update_config.py, update_config_0.4.x-0.5.py, upgrade.sh: update_config.py: adjusted functions stuff to renamed settings. Finally renamed update_config_0.4.x-0.5.py to update_config.py - no more version information in the filename. *.sh: removed .svn directory exclusion from find command. [bb58aedefa3a] * VirtualMailManager/Config.py, VirtualMailManager/VirtualMailManager.py, vmm, vmm.cfg: vmm.cfg: dealt with the most overdue section/option renaming. Added new options. Details: old new ------------------------------------------------------------ domdir.mode -> domain.directory_mode domdir.delete -> domain.delete_directory domdir.base -> misc.base_dir domdir -> _section domdir deleted_ maildir.mode -> account.directory_mode maildir.diskusage -> account.disk_usage maildir.delete -> account.delete_directory misc.forcedel -> domain.force_del misc.passwdscheme -> misc.password_scheme misc.dovecotvers -> misc.dovecot_vers services.smtp -> account.smtp services.pop3 -> account.pop3 services.imap -> account.imap services.sieve -> account.sieve services -> _section services deleted_ _NEW_.random_password -> account.random_password _NEW_.password_len -> account.password_len _NEW_.auto_postmaster -> domain.auto_postmaster [b152ad5c7071] 2010-01-04 Pascal Volk * INSTALL: INSTALL: Adapted pgsql configuration steps to that in the wiki. (Closes: #2922030) [7e50e4c49ed7] 2010-01-02 Pascal Volk * install.sh, upgrade.sh: *.sh: Added --force option to python setup.py install call. upgrade.sh: Removed old cleanup code. [67dc18294de6] * setup.py: setup.py: Fixed a UserWarning that may occur with older Python. [1981f285f0c0] * COPYING, VirtualMailManager/Account.py, VirtualMailManager/Alias.py, VirtualMailManager/AliasDomain.py, VirtualMailManager/Config.py, VirtualMailManager/Domain.py, VirtualMailManager/EmailAddress.py, VirtualMailManager/Exceptions.py, VirtualMailManager/MailLocation.py, VirtualMailManager/Relocated.py, VirtualMailManager/Transport.py, VirtualMailManager/VirtualMailManager.py, VirtualMailManager/__init__.py, VirtualMailManager/constants/ERROR.py, VirtualMailManager/constants/EXIT.py, VirtualMailManager/constants/VERSION.py, VirtualMailManager/constants/__init__.py, VirtualMailManager/ext/Postconf.py, VirtualMailManager/ext/__init__.py, man/de/man1/vmm.1, man/de/man5/vmm.cfg.5, man/man1/vmm.1, man/man5/vmm.cfg.5, po/de.po, setup.py, update_config_0.4.x-0.5.py, vmm: Updated copyright notices to include the year 2010. Also corrected the name of the copyright holder and updated the e-mail address. [0ac9ef587769] 2009-12-27 Pascal Volk * .hgtags: Removed the svn-ish tag trunk [b62aa4aabcd0] 2009-10-23 Pascal Volk * create_optional_types_and_functions-dovecot-1.2.x.pgsql, create_optional_types_and_functions.pgsql, create_tables- dovecot-1.2.x.pgsql, create_tables.pgsql, pgsql /create_optional_types_and_functions-dovecot-1.2.x.pgsql, pgsql/create_optional_types_and_functions.pgsql, pgsql /create_tables-dovecot-1.2.x.pgsql, pgsql/create_tables.pgsql, pgsql/update_tables_0.4.x-0.5.pgsql, pgsql/update_tables_0.5.x_for_dovecot-1.2.x.pgsql, pgsql/update_types_and_functions_0.5.x_for_dovecot-1.2.x.pgsql, update_tables_0.4.x-0.5.pgsql, update_tables_0.5.x_for_dovecot-1.2.x.pgsql, update_types_and_functions_0.5.x_for_dovecot-1.2.x.pgsql: *.pgsql: moved to pgsql/ [639cf4003965] 2009-10-22 Pascal Volk * install.sh, pgsql-relocated_maps.cf, pgsql- smtpd_sender_login_maps.cf, pgsql-transport.cf, pgsql- virtual_alias_maps.cf, pgsql-virtual_gid_maps.cf, pgsql- virtual_mailbox_domains.cf, pgsql-virtual_mailbox_maps.cf, pgsql- virtual_uid_maps.cf, postfix/pgsql-relocated_maps.cf, postfix/pgsql- smtpd_sender_login_maps.cf, postfix/pgsql-transport.cf, postfix /pgsql-virtual_alias_maps.cf, postfix/pgsql-virtual_gid_maps.cf, postfix/pgsql-virtual_mailbox_domains.cf, postfix/pgsql- virtual_mailbox_maps.cf, postfix/pgsql-virtual_uid_maps.cf, upgrade.sh: Moved Postfix PostgreSQL client configuration files into the postfix directory. [78b6b06188d3] * VirtualMailManager/VirtualMailManager.py, VirtualMailManager/constants/ERROR.py: VMM: Don't prompt endless for a password. Stop after 3rd failure. [6949f6eaf26e] 2009-10-20 Pascal Volk * po/vmm.pot, vmm: vmm: plan_a_b s/address/object/ [eb3ccf9484b3] * VirtualMailManager/Account.py, VirtualMailManager/VirtualMailManager.py, po/vmm.pot, vmm: Added comments for the Translation Project. Updated PO template. [a849843115e9] 2009-10-19 Pascal Volk * VirtualMailManager/Account.py, VirtualMailManager/Alias.py, VirtualMailManager/AliasDomain.py, VirtualMailManager/Domain.py, VirtualMailManager/EmailAddress.py, VirtualMailManager/Relocated.py, VirtualMailManager/VirtualMailManager.py, VirtualMailManager/ext/Postconf.py, vmm: Fixed some grammar errors and typos. (Reported by Clytie Siddall) [eb866ebb9f2e] 2009-10-11 Pascal Volk * po/vi.po: Added the Vietnamese translation to the repository. Many thanks to Clytie Siddall from the Translation Project for the work. [6e6488722807] 2009-09-29 Pascal Volk * po/nl.po: Updated Dutch translation (translated by Erwin Poeze). [f0386ee0c7e8] 2009-09-10 Pascal Volk * VirtualMailManager/__init__.py: Ignore unsupported locale setting and silently fall back to 'C'. [3dbee02711cd] 2009-09-09 Pascal Volk * .hgtags: Added tag vmm-0.5.2 for changeset 3e972996da7f [350488efe67d] * ChangeLog, NEWS, VirtualMailManager/constants/VERSION.py, setup.py: Released vmm-0.5.2 [3e972996da7f] [vmm-0.5.2] === 0.5.2 === 2009-09-09 Pascal Volk * vmm: Improved error handling in vmm's {alias}domaininfo. [581a5680d0ef] [tip] 2009-09-08 Pascal Volk * po/nl.po, setup.py: Added the Dutch translation to the repository. Many thanks to Erwin Poeze from the Translation Project for the work. [e574f5dedb60] * VirtualMailManager/Alias.py, vmm: Improved error handling in vmm's {user,alias,relocated}info. Removed some comparisons of string and Unicode. [e3fd0b67ae50] 2009-09-07 Pascal Volk * VirtualMailManager/Alias.py: Alias.__init__(): Check really if the given address is not used. [a08d78344706] * VirtualMailManager/Account.py: Sort alias addresses in Account.getAliases() / vmm userinfo. [ead2a7e9f8be] 2009-09-05 Pascal Volk * VirtualMailManager/Account.py, VirtualMailManager/Alias.py: Small optimizations in Account.getAliases() and Alias.getInfo(). [4c6aa6c29dd7] * VirtualMailManager/VirtualMailManager.py: Code cleanup/optimization in VMM's idn2ascii() and ace2idna(). [16542519a5a8] 2009-09-04 Pascal Volk * VirtualMailManager/Domain.py, vmm: Reduced the mixing/concatenating of str and unicode objects. Optimized list generation in Domain class. [28f26f7f3d8f] * VirtualMailManager/__init__.py: Small optimization in w_std()/w_err() 'avoid the dot'. [ecd6a379e523] 2009-09-03 Pascal Volk * VirtualMailManager/VirtualMailManager.py: Code cleanup/optimization in VirtualMailManager's __getSalt(). [ffac064bd728] 2009-09-02 Pascal Volk * po/fr.po, setup.py: Added the French translation to the repository. Many thanks to Dimitri Duc from the Translation Project for the work. [7f6911bd11cd] * VirtualMailManager/Account.py, VirtualMailManager/Alias.py, VirtualMailManager/AliasDomain.py, VirtualMailManager/Config.py, VirtualMailManager/Domain.py, VirtualMailManager/EmailAddress.py, VirtualMailManager/Exceptions.py, VirtualMailManager/MailLocation.py, VirtualMailManager/Relocated.py, VirtualMailManager/Transport.py, VirtualMailManager/VirtualMailManager.py, VirtualMailManager/__init__.py, VirtualMailManager/ext/Postconf.py, vmm: Reorganized module import. Dropped the import of constants.VERSION, when it wasn't really needed. Centralized the import of os, re and locale in VirtualMailManager/__init__.py. Also moved w_std() and w_err() from vmm to the __init__.py. [617f27715b01] 2009-09-01 Pascal Volk * vmm: Don't crash if the date format not only consists of ASCII signs. Added also the u stringprefix to translatable strings where it was missing. [fc09f657082d] 2009-08-31 Pascal Volk * VirtualMailManager/Config.py, VirtualMailManager/VirtualMailManager.py, vmm: Reorganized imports, eliminated a potential UnicodeEncodeError. Removed double definition of function w_std() in Config class. [fb61f64e6351] 2009-08-25 Pascal Volk * INSTALL, UPGRADE: Updated documentation [32ad0c79a6ef] * VirtualMailManager/Account.py: Fixed a Python2.4.4 SyntaxError [6ca3d22e5dd0] * VirtualMailManager/Account.py, VirtualMailManager/Alias.py, VirtualMailManager/AliasDomain.py, VirtualMailManager/Config.py, VirtualMailManager/Domain.py, VirtualMailManager/EmailAddress.py, VirtualMailManager/MailLocation.py, VirtualMailManager/Relocated.py, VirtualMailManager/VirtualMailManager.py, VirtualMailManager/ext/Postconf.py, man/de/man1/vmm.1, man/man1/vmm.1, po/de.po, po/vmm.pot, vmm: Replaced angle quotes by quotation marks. [2d5c4745efec] 2009-08-23 Pascal Volk * VirtualMailManager/VirtualMailManager.py, po/de.po, po/vmm.pot: Added a deprecated warning for managesieve. Updated translation. [fa22bd13b4d1] 2009-08-22 Pascal Volk * INSTALL, UPGRADE: Updated documentation [2b8154cc7ebe] * update_config_0.4.x-0.5.py: avoid error message if used with version 0.5.2 [9dca3e898ddb] 2009-08-21 Pascal Volk * .hgignore, VirtualMailManager/Alias.py, po/de.po, po/vmm.pot: Updated translation. [4ffb50de00d5] * INSTALL, UPGRADE, VirtualMailManager/Account.py, VirtualMailManager/Config.py, VirtualMailManager/Transport.py, VirtualMailManager/VirtualMailManager.py, VirtualMailManager/constants/ERROR.py, VirtualMailManager/ext/Postconf.py: Converted VirtualMailManager and Postconf to new-style classes. A few small cleanups. [cf8116625866] * INSTALL, UPGRADE: Updated documentation [97a9f6dd954b] * update_tables_0.5.x_for_dovecot-1.2.x.pgsql: removed GRANT statement. It's mentioned in UPGRADE [3643a0777e77] * update_tables_0.5.x_for_dovecot-1.2.x.pgsql: Removed the currently not needed WHERE clause from the VIEW. [988b9a719929] * create_optional_types_and_functions-dovecot-1.2.x.pgsql, create_tables-dovecot-1.2.x.pgsql: Added create SQL scripts for Dovecot v1.2.x [68af38212ff5] * update_tables_0.5.x_for_dovecot-1.2.x.pgsql, update_types_and_functions_0.5.x_for_dovecot-1.2.x.pgsql: Added update SQL scripts for Dovecot v1.2.x [626c008a4a04] 2009-08-20 Pascal Volk * VirtualMailManager/Alias.py, VirtualMailManager/AliasDomain.py, VirtualMailManager/Domain.py, VirtualMailManager/MailLocation.py, VirtualMailManager/Relocated.py: Converted to new-style class, added __slots__. [30abf0abf8f8] * VirtualMailManager/Account.py: Converted to new-style class, added __slots__, updated queries. Use the _uid attribute in queries whenever it is possible. [7ccc05774118] * VirtualMailManager/Config.py: Comments updated. [928659c8ee9f] * VirtualMailManager/VirtualMailManager.py, update_config_0.4.x-0.5.py: Small code cleanups (replaced dir+'/'+file by os.path.join()). [014335f38962] 2009-08-19 Pascal Volk * update_config_0.4.x-0.5.py, upgrade.sh: Rewrote upgrade script and config update script [cf85d78486ce] * setup.py: Updated long_description, download_url and platforms. Dropped VirtualMailManager.constants.VERSION import - error-prone. [c96b5768c76d] 2009-08-18 Pascal Volk * VirtualMailManager/EmailAddress.py: Fixed a logical mistake in EmailAddress.__ne__() (not used), small code cleanups. [949c5db6447a] * VirtualMailManager/Account.py, VirtualMailManager/VirtualMailManager.py, vmm: Added sieve/managesieve switching stuff, depending on the used Dovecot version. [21f264a88ab2] * VirtualMailManager/Account.py: Fixed a libpq.OperationalError in Account.delete() Passing only the object's address string to the 'alias delete' query, not the whole EmailAddress object. [e671210b04b8] 2009-08-17 Pascal Volk * VirtualMailManager/Account.py, VirtualMailManager/Config.py, VirtualMailManager/VirtualMailManager.py, man/de/man1/vmm.1, man/de/man5/vmm.cfg.5, man/man1/vmm.1, man/man5/vmm.cfg.5, vmm, vmm.cfg: Config: renamed services.managesieve to services.sieve, added misc.dovecotvers [e35755191ff3] * VirtualMailManager/Config.py: Hopefully the final UnicodeDecodeError fix for the Config class Added a global w_std() function and replaced all print statements. [d1f345f91e1c] 2009-08-16 Pascal Volk * ChangeLog: Added the essential changes since vmm-0.4. Partly reformatted. [50ff50f5055e] 2009-08-14 Pascal Volk * VirtualMailManager/Domain.py, VirtualMailManager/Transport.py: Transport: converted to new-style class; Domain: query reduction VirtualMailManager/Transport.py: * added: __slots__ * implemented: __eq__(), __ne__() and __str__() VirtualMailManager/Domain.py (updateTransport()): * reduced db lookups/update [cb8b2f6a5fca] * VirtualMailManager/VirtualMailManager.py: VMM: Fixed an AttributeError, caused by the last commit. Class EmailAddress has no __dict__ anymore. [766299a8639d] 2009-08-13 Pascal Volk * VirtualMailManager/EmailAddress.py: VMM.EmailAddress: Added __slots__ aka post-sf.net-hg-update-commit [fb88585f17fe] === 0.5.1 === 2009-08-12 Pascal Volk * .hgtags: Added tag vmm-0.5.1 for changeset dc98cc162c66 [e98a0fdf1266] * NEWS, UPGRADE, VirtualMailManager/constants/VERSION.py: Released vmm-0.5.1 [dc98cc162c66] [vmm-0.5.1] * pgsql-relocated_maps.cf, pgsql-smtpd_sender_login_maps.cf, pgsql-transport.cf, pgsql-virtual_alias_maps.cf, pgsql-virtual_gid_maps.cf, pgsql-virtual_mailbox_domains.cf, pgsql-virtual_mailbox_maps.cf, pgsql-virtual_uid_maps.cf: Set default value of hosts to localhost Updated comments 2009-08-09 Pascal Volk * VirtualMailManager/Config.py: Replaced the last non encoded print statement. * install.sh, upgrade.sh: Do now strict POSIX compliant string comparison in expressions. Fixed a typo in upgrade.sh. * nearly all files: Removed the subversion keywords $Date$, $Id$ and $Rev$ from all files. 2008-12-27 Pascal Volk * VirtualMailManager/Config.py: Config.configure(): Eliminated another UnicodeEncodeError. * vmm.cfg: removed single quotes around bin.postconf Thanks to samfisch for reporting both bugs 2008-12-23 Pascal Volk * VirtualMailManager/Config.py: fixed parenthesis in Config.configure() * setup.py: adjusted long_description updated url to http://vmm.localdomain.org/ * vmm: fixed UnicodeDecodeError, that could occur with German locale 2008-12-08 Pascal Volk * VirtualMailManager/VirtualMailManager.py,VirtualMailManager/Config.py: Added support for variable "vmm.cfg" location in /root:/usr/local/etc:/etc === 0.5 === 2008-11-26 Pascal Volk * NEWS: Added to repository. News (enhancements, bug fixes) for non programmers * VirtualMailManager/constants/VERSION.py: Updated version from 0.5-dev to 0.5 2008-09-16 Pascal Volk * VirtualMailManager/ext/Postconf.py: Added to repository to read some Postfix settings 2008-09-09 Pascal Volk * VirtualMailManager/Domain.py, vmm, VirtualMailManager/VirtualMailManager.py: Added relocated stuff 2008-09-08 Pascal Volk * VirtualMailManager/EmailAddress.py: Added to repository, to simplify/reduce address validation 2008-09-01 Pascal Volk * create_optional_types_and_functions.pgsql: Added to repository, for faster database lookups 2008-08-29 Pascal Volk * VirtualMailManager/VirtualMailManager.py: Fixed DIGEST-MD5 hash generation. 2008-08-23 Pascal Volk * VirtualMailManager/Exceptions.py: Fixed AttributeError in class VMMException on older Python installations. (VMMException instance has no attribute 'message') 2008-08-22 Pascal Volk * VirtualMailManager/Domain.py, VirtualMailManager/AliasDomain.py VirtualMailManager/VirtualMailManager.py: did some alias domain modifications/enhancements * vmm: Implemented w_std() to write encoded output to stdout 2008-08-19 Pascal Volk * VirtualMailManager/Config.py. VirtualMailManager/VirtualMailManager.py, vmm: Renamed class VMMConfig -> Config Adjusted Config import 2008-08-16 Pascal Volk * VirtualMailManager/VirtualMailManager.py, VirtualMailManager/Domain.py: Added alias domain stuff 2008-08-14 Pascal Volk * VirtualMailManager/Account.py, VirtualMailManager/Domain.py: adjusted to modified database layout * create_tables.pgsql, update_tables_0.4.x-0.5.pgsql: modified database layout 2008-08-07 Pascal Volk * VirtualMailManager/Account.py: Account.getInfo() added i18n stuff 2008-08-06 Pascal Volk * vmm (_getOrder): Respect vmm.cfg/maildir/diskusage settings * VirtualMailManager/VirtualMailManager.py: Adds a warning if a directory not exists: VirtualMailManager.__getDiskUsage() VirtualMailManager.__maildirdelete() VirtualMailManager.__domdirdelete() Implemented: VirtualMailManager.__isdir() VirtualMailManager.cfgGetBoolean() VirtualMailManager.cfgGetInt() VirtualMailManager.cfgGetString() * po/vmm.pot, po/de.po: Updated 2008-06-17 Pascal Volk * install.sh, upgrade.sh: fixed $MANDIR * vmm: removed BEL escape sequences 2008-06-11 Pascal Volk * install.sh: removed verbose stuff fixed (multilingual) man pages installation stuff * man/man1/vmm1, man/man5/vmm.cfg.5: moved to sub folders 2008-05-25 Pascal Volk * VirtualMailManager/VirtualMailManager.py (VirtualMailManager): Renamed: __idn2ascii() -> idn2ascii() __ace2idna() -> ace2idna() Implemented domain_list() some small code cleanups * vmm: Implemented domain_list() some small code cleanups * VirtualMailManager/Domain.py: Implemented search() 2008-05-18 Pascal Volk * VirtualMailManager/Account.py, VirtualMailManager/Alias.py, VirtualMailManager/Config.py, VirtualMailManager/Domain.py, VirtualMailManager/MailLocation.py, VirtualMailManager/Transport.py, VirtualMailManager/VirtualMailManager.py, po/de.po, po/vmm.pot, vmm: completed gettext support 2008-05-15 Pascal Volk * vmm.cfg: now vmm.cfg conforms to example in INSTALL. 2008-05-13 Pascal Volk * VirtualMailManager/Alias.py, VirtualMailManager/MailLocation.py, VirtualMailManager/Account.py, VirtualMailManager/Transport.py, VirtualMailManager/VirtualMailManager.py, VirtualMailManager/Config.py, VirtualMailManager/Domain.py, vmm: Added i18n (gettext) support * install.sh: Install message objects 2008-05-10 Pascal Volk * install.sh: Should now also work on all *BSDs 2008-05-01 Pascal Volk * VirtualMailManager/VirtualMailManager.py (VirtualMailManager.__chkenv()): Create the base directory with correct access mode and gid, ifit does not exist yet. * update_config_0.3.x-0.4.py, update_tables_0.3.x-0.4.py, UPGRADE, upgrade.sh: removed because vmm 0.4 was released === 0.4-r41 === 2008-05-01 Pascal Volk * create_tables.pgsql: fixed typo in CREATE TABLE users * install.sh, upgrade.sh: Added again $PF_GID (was removed with r19) 2008-04-30 Pascal Volk * UPGRADE: added hint for virtual_mailbox_domains in UPGRADE === 0.4 === 2008-04-30 Pascal Volk * vmm.1, vmm.cfg.5: Reworded some parts. * vmm: Added function _getOrder() for a (hopefully) better structured output from the domaininfo, userinfo and getuser subcommands. 2008-04-28 Pascal Volk * vmm.cfg: * update_config_0.3.x-0.4.py: * VirtualMailManager/VirtualMailManager.py: * VirtualMailManager/Config.py: Moved option 'base' from section 'maildir' to section 'domdir' * VirtualMailManager/Account.py (Account._setAddr()): Removed parameter 'address' * VirtualMailManager/Domain.py (Domain.getAccounts(), Domain.getAliases()): Added 'ORDER BY' clause in queries. + setup.py: Adjusted trove classifiers. 2008-04-21 Pascal Volk * create_tables.pgsql (table users): Replaced column 'disabled' with columns smpt, pop3, imap and managesieve * create_tables.pgsql (view dovecot_password): updated view, added service columns smpt, pop3, imap and managesieve * update_tables_0.3.x-0.4.py: Updated to consider the points above mentioned * vmm.cfg: Added section »services« with options: smtp, pop3, imap and managesieve * update_config_0.3.x-0.4.py, VirtualMailManager/Config.py: Updated, to add new section »services« * VirtualMailManager/Account.py (Account._switchState(), Account.enable(), Account.disable(), Account.save(), Account.getInfo()), VirtualMailManager/VirtualMailManager.py (VirtualMailManager.user_add(), VirtualMailManager.user_disable(), VirtualMailManager.user_enable()), vmm (usage(), user_disable(), user_enable()): Modified, to fit new database structure * UPGRADE, INSTALL: Updated information * update_tables_0.4-dev_r24.py: Added temporary to the repository 2008-04-18 Pascal Volk * VirtualMailManager/Account.py: Implemented getAccountByID() * VirtualMailManager/VirtualMailManager.py (VirtualMailManager): * vmm: Implemented user_byID() 2008-04-15 Pascal Volk * VirtualMailManager/Account.py (Account.modify()): * vmm (main): Added code for modify user's transport * VirtualMailManager/VirtualMailManager.py: Implemented VirtualMailManager.user_transport() * VirtualMailManager/VirtualMailManager.py (VirtualMailManager.domain_transport()): * vmm: * VirtualMailManager/Domain.py (Domain.updateTransport()): Added code to optional force new transport for all existing accounts. 2008-04-14 Pascal Volk * VirtualMailManager/VirtualMailManager.py: Implemented: VirtualMailManager.__getSalt(), VirtualMailManager.__pwCrypt(), VirtualMailManager.__pwSHA1(), VirtualMailManager.__pwMD5() and VirtualMailManager.__pwMD4() updated VirtualMailManager.__pwhash() * VirtualMailManager/constants/VERSION.py: Set Version to 0.4-dev * INSTALL: Added hint for python-crypto, fixed user_query 2008-04-10 Pascal Volk * update_tables_0.3.x-0.4.py: Replaced view dovecot_user * create_tables.pgsql (VIEW: dovecot_user): Added extra field mail * UPGRADE: fixed filename, hint for view permissions and user_query * INSTALL: Replaced mail_extra_groups with mail_privileged_group, updated user_query 2008-04-06 Pascal Volk * install.sh: Removed $PF_GID * INSTALL: Added hints for pyPgSQL and smtpd_recipient_restrictions * update_config_0.3.x-0.4.py: * upgrade.sh: * UPGRADE: Added to repository * VirtualMailManager/VirtualMailManager.py (VirtualMailManager.__init__): Don't call VirtualMailManager.__chkenv() if vmm is started with option configure. * VirtualMailManager/VirtualMailManager.py (VirtualMailManager.__chkenv): Added value for placeholder in error message. 2008-03-05 Pascal Volk * create_tables.pgsql: Removed unneeded newlines from views dovecot_user and postfix_uid * update_tables_0.2.x-0.3.pgsql: Removed from repository * update_tables_0.3.x-0.4.py: Added to repository 2008-03-04 Pascal Volk * VirtualMailManager/Alias.py: * VirtualMailManager/VirtualMailManager.py: * vmm: Now it's possible to delete an alias with a specific destination 2008-03-03 Pascal Volk * pgsql-virtual_mailbox_domains.cf: Added to repository * INSTALL: fixed postfix docu 2008-03-02 Pascal Volk * create_tables.pgsql: Added view vmm_domain_info * VirtualMailManager/Alias.py: Removed attribute Alias._aid Removed parameter basedir from Alias.__init__() and Alias._setAddr() * VirtualMailManager/MailLocation.py: Fixed typo in MailLocation.__init__() * VirtualMailManager/Account.py: Integrated Transport- and MailLocation-stuff Removed attributes: Account._base and Account._home * VirtualMailManager/VirtualMailManager.py: some small fixes * VirtualMailManager/Domain.py: Added Transport-stuff * vmm.cfg: Added option transport in section misc * pgsql-transport.cf: changed query for new table layout * pgsql-smtpd_sender_login_maps.cf: added missing single quote in query 2008-02-02 Pascal Volk * create_tables: Renamed table maildir to maillocation Added transport id to table domains, for default transport * VirtualMailManager/Transport.py: * VirtualMailManager/MailLocation.py: Added to repository * VirtualMailManager/constants/ERROR.py: Added error codes for MailLocation and Transport classes * VirtualMailManager/Exceptions.py: Added exception classes for MailLocation and Transport 2008-01-18 Pascal Volk * create_tables.pgsql: Removed unneeded sequences 'alias_id' and 'relocated_id'. Removed unneeded column 'id' from table 'alias' and 'relocated'. 2008-01-15 Pascal Volk * create_tables.pgsql: Redesign of table layout, create separate tables for maildir-folder and transport. Also assign transport to users not to domains 2008-01-11 Pascal Volk * vmm (usage): Fixed a syntax error. 2008-01-09 Pascal Volk * install.sh: Also install docs * VirtualMailManager/Account.py: * VirtualMailManager/Alias.py: * VirtualMailManager/Config.py: * VirtualMailManager/Domain.py: * VirtualMailManager/Exceptions.py: * VirtualMailManager/VirtualMailManager.py: * setup.py: * vmm: Included global version number. * VirtualMailManager/constants/VERSION.py: Added to repository. 2008-01-09 Pascal Volk * VirtualMailManager/Account.py: * VirtualMailManager/Alias.py: * VirtualMailManager/Domain.py: * VirtualMailManager/VirtualMailManager.py: * vmm: Replaced email with e-mail. Corrected opening/closing quotation marks («word» -> »word«). * VirtualMailManager/VirtualMailManager.py: Renamed VirtualMailManager.__chkEmailadress to VirtualMailManager.__chkEmailAddress. * ChangeLog: Added to repository. === 0.3.1 === 2008-01-08 Pascal Volk * vmm (getVMM): Fixed names names of excepted errors. * VirtualMailManager/VirtualMailManager.py (VirtualMailManager.__init__): activated check for missing sections/options * vmm-0.3.1.tar.bz2: It's really bzip2 compressed. ;-) vmm-0.6.1/setup.cfg0000644000175000017500000000007412033032424012322 0ustar pvopvo[install] compile = 1 optimize = 1 [sdist] formats = bztar vmm-0.6.1/UPGRADE0000644000175000017500000000667512033032424011530 0ustar pvopvoIf you still have installed vmm 0.4.x you have to proceed this step first: * upgrade your vmm installation to version 0.5.2 If you have installed vmm 0.5.2 you have to proceed this steps: * stop Postfix and Dovecot * backup/dump your database. * backup/dump your database! * start psql and connect to the appropriate database (ex. psql mailsys vmm -W -h 127.0.0.1) * update the database, - Dovecot < 1.2.0 \i vmm-x.y.z/pgsql/update_tables_0.5.x-0.6.pgsql - Dovecot >= 1.2.0, 2.0.0 and 2.1.0 \i vmm-x.y.z/pgsql/update_tables_0.5.x-0.6-dovecot-1.2.x.pgsql * Set database permissions. (see python set-permissions.py -h for details) python vmm-x.y.z/pgsql/set-permissions.py -a -H 127.0.0.1 -U vmm /!\ Important note /!\ All the views (dovecot_* and postfix_*) have been replaced by database functions. So you have to adjust all your postfix/pgsql-*.cf files and also your /etc/dovecot/dovecot-sql.conf or /etc/dovecot/dovecot-sql.conf.ext. (See the vmm-x.y.z postfix/pgsql-*.cf files and INSTALL/Configure.Dovecot_2 files for the new query.) * execute upgrade.sh This will also upgrade your vmm.cfg and apply the following modifications: old new ------------------------------------------------------------ domdir.mode -> domain.directory_mode domdir.delete -> domain.delete_directory domdir.base -> misc.base_directory domdir -> _section domdir deleted_ maildir.mode -> account.directory_mode maildir.diskusage -> account.disk_usage maildir.delete -> account.delete_directory maildir.folders -> mailbox.folders maildir.name -> mailbox.root maildir -> _section maildir deleted_ misc.forcedel -> domain.force_deletion misc.transport -> domain.transport misc.passwdscheme -> misc.password_scheme misc.dovecotvers -> misc.dovecot_version (12 -> 1.2.11) misc.gid_mail -> /dev/null services.smtp -> domain.smtp services.pop3 -> domain.pop3 services.imap -> domain.imap services.sieve -> domain.sieve services -> _section services deleted_ _NEW_.random_password -> account.random_password _NEW_.password_length -> account.password_length _NEW_.auto_postmaster -> domain.auto_postmaster _NEW_.quota_bytes -> domain.quota_bytes _NEW_.quota_messages -> domain.quota_messages _NEW_.module -> database.module _NEW_.port -> database.port _NEW_.sslmode -> database.sslmode _NEW_.format -> mailbox.format _NEW_.crypt_blowfish_rounds -> misc.crypt_blowfish_rounds _NEW_.crypt_sha256_rounds -> misc.crypt_sha256_rounds _NEW_.crypt_sha512_rounds -> misc.crypt_sha512_rounds config.done -> /dev/null config -> _section config deleted_ * start Dovecot and Postfix again If you have installed vmm 0.6.0 you have to proceed this steps: * Database fixes: - Due to an error in usertransport's argument parsing, it is possible that some users' transport-ID points to the erroneous transport 'domain'. To fix that error in your database, execute the following SQL statement: UPDATE users SET tid = NULL WHERE tid = (SELECT tid FROM transport WHERE transport = 'domain'); - If you are using Dovecot < v1.2.0: You have to replace the database FUNCTION dovecotpassword(). (see file: pgsql/create_tables.pgsql) The service_set.ssid was selected unconditionally. This may cause an empty result, which will make logins impossible. * execute upgrade.sh else * read INSTALL vmm-0.6.1/install.sh0000755000175000017500000000444612033032424012515 0ustar pvopvo#!/bin/sh # # Installation script for the Virtual Mail Manager # run: ./install.sh LANG=C PATH=/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin PREFIX=/usr/local PF_CONFDIR=$(postconf -h config_directory) PF_GID=$(id -g $(postconf -h mail_owner)) LOCALE_DIR=${PREFIX}/share/locale DOC_DIR=${PREFIX}/share/doc/vmm if [ ${PREFIX} = "/usr" ]; then MANDIR=${PREFIX}/share/man else MANDIR=${PREFIX}/man fi DOCS="ChangeLog Configure.Dovecot_2 COPYING INSTALL NEWS README" INSTALL_OPTS="-g 0 -o 0 -p" INSTALL_OPTS_CF="-b -m 0640 -g ${PF_GID} -o 0 -p" if [ $(id -u) -ne 0 ]; then echo "Run this script as root." exit 1 fi python setup.py -q install --force --prefix ${PREFIX} python setup.py clean --all >/dev/null install -b -m 0600 ${INSTALL_OPTS} vmm.cfg ${PREFIX}/etc/ install ${INSTALL_OPTS_CF} postfix/pgsql-*.cf ${PF_CONFDIR}/ install -m 0700 ${INSTALL_OPTS} vmm ${PREFIX}/sbin [ -d ${LOCALE_DIR} ] || mkdir -m 0755 -p ${LOCALE_DIR} cd po for po in $(ls -1 *.po); do lang=$(basename ${po} .po) ddir=${LOCALE_DIR}/${lang}/LC_MESSAGES [ -d ${ddir} ] || mkdir -m 0755 -p ${ddir} msgfmt -o ${LOCALE_DIR}/${lang}/LC_MESSAGES/vmm.mo ${po} done cd - >/dev/null cd man [ -d ${MANDIR}/man1 ] || mkdir -m 0755 -p ${MANDIR}/man1 install -m 0644 ${INSTALL_OPTS} man1/vmm.1 ${MANDIR}/man1 [ -d ${MANDIR}/man5 ] || mkdir -m 0755 -p ${MANDIR}/man5 install -m 0644 ${INSTALL_OPTS} man5/vmm.cfg.5 ${MANDIR}/man5 for l in $(find . -maxdepth 1 -mindepth 1 -type d \! -name man\?) do for s in man1 man5; do [ -d ${MANDIR}/${l}/${s} ] || mkdir -m 0755 -p ${MANDIR}/${l}/${s} done if [ -f ${l}/man1/vmm.1 ]; then install -m 0644 ${INSTALL_OPTS} ${l}/man1/vmm.1 ${MANDIR}/${l}/man1 fi if [ -f ${l}/man5/vmm.cfg.5 ]; then install -m 0644 ${INSTALL_OPTS} ${l}/man5/vmm.cfg.5 ${MANDIR}/${l}/man5 fi done cd - >/dev/null [ -d ${DOC_DIR} ] || mkdir -m 0755 -p ${DOC_DIR} for DOC in ${DOCS}; do install -m 0644 ${INSTALL_OPTS} ${DOC} ${DOC_DIR} done [ -d ${DOC_DIR}/examples ] || mkdir -m 0755 -p ${DOC_DIR}/examples install -m 0644 ${INSTALL_OPTS} postfix/pgsql-*.cf ${DOC_DIR}/examples install -m 0644 ${INSTALL_OPTS} vmm.cfg ${DOC_DIR}/examples echo echo "Don't forget to edit ${PREFIX}/etc/vmm.cfg - or run: vmm cf" echo "and ${PF_CONFDIR}/pgsql-*.cf files." echo vmm-0.6.1/man/0000755000175000017500000000000012033032424011253 5ustar pvopvovmm-0.6.1/man/de/0000755000175000017500000000000012033032424011643 5ustar pvopvovmm-0.6.1/man/de/man1/0000755000175000017500000000000012033032424012477 5ustar pvopvovmm-0.6.1/man/de/man1/vmm.10000644000175000017500000007135712033032424013375 0ustar pvopvo.TH "VMM" "1" "2012-09-27" "vmm 0.6" "vmm" .SH NAME vmm \- Kommandozeilenprogramm zur Verwaltung von E\-Mail\-Domains/\-Konten und \-Aliase. .\" ----------------------------------------------------------------------- .SH ÜBERSICHT .B vmm .IR Unterbefehl " [" "Argument ..." ] .\" ----------------------------------------------------------------------- .SH BESCHREIBUNG .B vmm (a virtual mail manager) ist das einfach zu bedienende Kommandozeilenprogramm für Administratoren und Postmaster, zur Verwaltung von (Alias\-) Domains, Konten, Alias\-Adressen und sogenannten Relocated Users. Es ermöglicht die schnelle und einfache Verwaltung des Mailservers. .br Es wurde für Dovecot und Postfix mit einem PostgreSQL\-Backend entwickelt. .PP Von jedem .I Unterbefehl gibt es jeweils eine lange und kurze Variante. Die Kurzform ist in Klammern geschrieben. Bei beiden Formen ist die Groß\-/Kleinschreibung zu berücksichtigen. .PP Die meisten .IR Unterbefehl e erwarten ein oder mehrere .IR Argument e. .\" ----------------------------------------------------------------------- .SH ARGUMENTE .TP 12 .I address Die komplette E\-Mail\-Adresse .RI ( local\-part @ fqdn ) eines Kontos, Aliases oder Relocated Users. .\" -------------------------- .TP .I destination Ist entweder eine E\-Mail\-Adresse, wenn sie in Verbindung mit .I "ALIAS UNTERBEFEHLEN" verwendet wird, oder ein .I fqdn in Verbindung mit .IR "ALIASDOMAIN UNTERBEFEHLEN" . .\" -------------------------- .TP .I fqdn Der voll qualifizierten Domain\-Namen \(em ohne den abschließenden Punkt \(em einer Domain oder Alias\-Domain. .\" -------------------------- .TP .I messages Ein Integer\-Wert, der das maximal nutzbare Kontingent als Anzahl von Nachrichten festlegt. .br Der Wert .B 0 (null) bedeutet unbegrenzt \(em kein Quota\-Limit als Anzahl von Nachrichten. .\" -------------------------- .TP .I option ist der Name einer Konfigurationsoption mit vorangestellter Konfigurations\-Sektion, getrennt durch einen Punkt. Zum Beispiel: .IB misc . transport .br Alle Konfigurationsoptionen werden in .BR vmm.cfg (5) beschrieben. .\" -------------------------- .TP .I service Der Name eines Services, der gewöhnlicherweise in Verbindung mit Dovecot genutzt wird. Folgende Services werden unterstützt: .BR imap ", " pop3 ", " sieve " und " smtp . .\" -------------------------- .TP .I storage Bestimmt das maximal nutzbare Kontingent in Bytes. Eines der folgenden Präfixe kann dem dem ganzzahligen Wert angehängt werden: .BR b " (Bytes), " k " (Kilobytes), " M " (Megabytes) oder " G (Gigabytes). .br Der Wert .B 0 (null) bedeutet unbegrenzt \(em kein Quota\-Limit in Bytes. .\" -------------------------- .TP .I transport ein Transport für Postfix, angegeben in der Form: .IB transport : oder .IB transport :\c .IR nexthop . Siehe .BR transport (5) für weitere Details. .\" ----------------------------------------------------------------------- .SH ALLGEMEINE UNTERBEFEHLE .SS configget (cg) .BI "vmm configget" " option" .PP Dieser Unterbefehl wird verwendet, um den aktuellen Wert der übergebenen .I option anzuzeigen. .PP Beispiel: .PP .nf .B vmm configget misc.crypt_sha512_rounds misc.crypt_sha512_rounds = 5000 .fi .\" -------------------------- .SS configset (cs) .B vmm configset .I option value .PP Verwenden Sie diesen Unterbefehl, um einer einzelnen Konfigurationsoption einen neuen Wert zuzuweisen. .I option ist der Name der Konfigurationsoption, .I value ist der Wert, der der Konfigurationsoption zugewiesen wird. .IP Hinweis: Diese Unterbefehl erstellt eine neue .IR vmm.cfg , ohne Kommentare. Die aktuelle Konfigurationsdatei wird als .IR vmm.cfg.bak gesichert. .PP Beispiel: .PP .nf .B vmm configget domain.transport domain.transport = dovecot: .B vmm configset domain.transport lmtp:unix:private/dovecot\-lmtp .B vmm cg domain.transport domain.transport = lmtp:unix:private/dovecot\-lmtp .fi .\" ------------------------------------ .SS configure (cf) .B vmm configure .RI [ section ] .PP Startet die interaktiven Konfiguration für alle Konfigurationssektionen. .PP Dabei wird der aktuell konfigurierte Wert einer jeden Option in eckigen Klammern ausgegeben. Sollte kein Wert konfiguriert sein, wird der Vorgabewert der jeweiligen Option in in eckigen Klammern angezeigt. Um den angezeigten Wert unverändert zu übernehmen, ist dieser mit der Eingabe\-Taste zu bestätigen. .PP Wurde das optionale Argument .I section angegeben, werden nur die Optionen der angegebenen Sektion angezeigt und können geändert werden. Folgende Sektionen sind vorhanden: .RS .TP 10 .B account Konto Einstellungen .TP .B bin Pfade zu externen Binär\-Dateien .TP .B database Datenbank Einstellungen .TP .B domain Domain Einstellungen .TP .B mailbox Mailbox Einstellungen .TP .B misc Verschiedene Einstellungen .RE .PP Die Konfigurationsoptionen werden in .BR vmm.cfg (5) beschrieben. .IP Hinweis: Diese Unterbefehl erstellt eine neue .IR vmm.cfg , ohne Kommentare. Die aktuelle Konfigurationsdatei wird als .IR vmm.cfg.bak gesichert. .PP Beispiel: .PP .nf .B vmm configure mailbox Konfigurationsdatei wird verwendet: /root/vmm.cfg * Konfigurationsabschnitt: »mailbox« Neuer Wert für Option folders [Drafts:Sent:Templates:Trash]: Neuer Wert für Option format [maildir]: mdbox Neuer Wert für Option subscribe [True]: Neuer Wert für Option root [Maildir]: mdbox .fi .\" ------------------------------------ .SS getuser (gu) .BI "vmm getuser" " uid" .PP Wenn nur der .I uid eines Benutzers vorhanden ist, zum Beispiel aus der Prozessliste, kann mit dem Unterbefehl .B getuser die E\-Mail\-Adresse des Benutzers ermittelt werden. .PP Beispiel: .PP .nf .B vmm getuser 79876 Konto Informationen ------------------- UID............: 79876 GID............: 70704 Address........: a.user@example.com .fi .\" ------------------------------------ .SS help (h) .B vmm help .RI [ subcommand ] .PP Gibt ein Liste aller vorhandenen Unterbefehle mit einer kurzen Beschreibung aus. Wurde ein .I subcommand angegeben, wird Hilfe zu diesem Unterbefehl ausgegeben. Danach wird .B vmm beendet. .\" ------------------------------------ .SS listdomains (ld) .B vmm listdomains .RI [ pattern ] .PP Dieser Unterbefehl listet alle angelegten Domains auf. Allen Domains wird ein Präfix vorangestellt. Entweder ein `[+]', falls es sich um eine primäre Domain handelt, oder ein `[-]', falls es sich um eine Alias\-Domain handelt. Die Ausgabe kann reduziert werden, indem ein optionales Muster angegeben wird. .PP Um eine Wildcard\-Suche durchzuführen kann das %\-Zeichen am Anfang und/oder Ende des Musters verwendet werden. .PP Beispiel: .PP .nf .B vmm listdomains %example% Übereinstimmende Domains ------------------------ [+] example.com [\-] e.g.example.com [\-] example.name [+] example.org [+] sales.example.com .fi .\" ------------------------------------ .SS listaddresses (ll) .B vmm listaddresses .RI [ pattern ] .PP Verwenden Sie diesen Unterbefehl, um alle Adressen anzuzeigen. Einträge regulärer Domains werden mit einem '+' gekennzeichnet, Einträge von Alias-Domains mit einem '-'. Zudem wird jedem Eintrag ein 'u', 'a', oder 'r' vorangestellt, welche den Eintrag als Benutzerkonto, Alias oder Relocated User identifizieren. .PP Mit dem optionalen Muster können Sie die Ausgabe einschränken. Akzeptiert wird entweder eine Domain oder ein SQL-Muster (% als Wildcard). .PP Beispiel: .PP .nf .B vmm listaddresses example.com .B vmm listaddresses %master@% .\" ------------------------------------ .SS listaliases (la) .B vmm listaliases .RI [ pattern ] .PP Verwenden Sie diesen Unterbefehl, um alle Aliase anzuzeigen. Reguläre Aliase werden mit einem '+' gekennzeichnet, Aliase in Alias-Domains mit einem '-'. .PP Mit dem optionalen Muster können Sie die Ausgabe einschränken. Akzeptiert wird entweder eine Domain oder ein SQL-Muster (% als Wildcard). .PP Beispiel: .PP .nf .B vmm listaliases example.com .B vmm listaliases %master@% .\" ------------------------------------ .SS listrelocated (lr) .B vmm listrelocated .RI [ pattern ] .PP Verwenden Sie diesen Unterbefehl, um alle Relocated Users anzuzeigen. Einträge regulärer Domains werden mit einem '+' gekennzeichnet, Einträge von Alias-Domains mit einem '-'. .PP Mit dem optionalen Muster können Sie die Ausgabe einschränken. Akzeptiert wird entweder eine Domain oder ein SQL-Muster (% als Wildcard). .PP Beispiel: .PP .nf .B vmm listrelocated example.com .B vmm listrelocated %master@% .\" ------------------------------------ .SS listusers (lu) .B vmm listusers .RI [ pattern ] .PP Verwenden Sie diesen Unterbefehl, um alle Benutzerkonten anzuzeigen. Reguläre Benutzerkonten werden mit einem '+' gekennzeichnet, Benutzerkonten in Alias-Domains mit einem '-'. .PP Mit dem optionalen Muster können Sie die Ausgabe einschränken. Akzeptiert wird entweder eine Domain oder ein SQL-Muster (% als Wildcard). .PP Beispiel: .PP .nf .B vmm listusers example.com .B vmm listusers %master@% .\" ------------------------------------ .SS listpwschemes (lp) .B vmm listpwschemes .PP Dieser Unterbefehl listet alle unterstützte Passwort\-Schemen, die als Wert für .I misc.password_scheme in der .I vmm.cfg verwendet werden können. Die Ausgabe variiert, je nach eingesetzter Dovecot Version und der libc des Systems. .br Sollte Ihre Dovecot\-Installation nicht zu alt sein, werden zusätzlich die verwendbaren Encoding\-Suffixe ausgegeben. Eines dieser Suffixe kann an das Passwort\-Schema angefügt werden. .PP Beispiel: .PP .nf .B vmm listpwschemes Verfügbare Passwort-Schemata ---------------------------- CRYPT SHA512-CRYPT LDAP-MD5 DIGEST-MD5 SHA256 SHA512 SSHA512 SKEY SSHA NTLM RPA MD5-CRYPT HMAC-MD5 SHA1 PLAIN SHA CRAM-MD5 SSHA256 MD5 LANMAN CLEARTEXT PLAIN-MD5 PLAIN-MD4 OTP SMD5 SHA256-CRYPT Verwendbare Encoding-Suffixe ---------------------------- .B64 .BASE64 .HEX .fi .\" ------------------------------------ .SS version (v) .B vmm version .PP Gibt Versions\- und Copyright\-Informationen zu .B vmm aus. Danach wird .B vmm beendet. .\" ----------------------------------------------------------------------- .SH DOMAIN UNTERBEFEHLE .SS domainadd (da) .B vmm domainadd .IR fqdn " [" transport ] .PP Fügt eine neue Domain in die Datenbank ein und erstellt das Domain\-Verzeichnis. .PP Wurde das optional Argument .I transport angegeben, ersetzt der angegebene Transport den konfigurierten Transport .RI ( misc.transport ") aus " vmm.cfg . Der angegebene .I transport ist der Vorgabe\-Transport für alle Konten, die dieser Domain zugeordnet werden. .PP Konfigurationsbezogenes Verhalten: .RS .TP .I domain.auto_postmaster Wenn diese Option den Wert .B true (Vorgabe) hat, wird .B vmm nach erfolgreichem Anlegen der Domain auch das Konto für .BI postmaster@ fqdn erstellen. .TP .I account.random_password Sollte dieser Option ebenfalls der Wert .B true zugewiesen sein, wird ein zufällig generiertes Passwort für den Postmaster\-Account gesetzt und auf stdout ausgegeben. .RE .PP Beispiele: .PP .nf .B vmm domainadd support.example.com smtp:[mx1.example.com]:2025 Konto für postmaster@support.example.com wird angelegt Neues Passwort eingeben: Neues Passwort wiederholen: .B vmm cs account.random_password true .B vmm domainadd vertrieb.example.com Konto für postmaster@vertrieb.example.com wird angelegt Erzeugtes Passwort: YoG3Uw*5aH .fi .\" ------------------------------------ .SS domaindelete (dd) .BI "vmm domaindelete " fqdn .RB [ force ] .PP Dieser Unterbefehl löscht die Domain mit dem angegebenen .IR fqdn . .PP Sollten der zu löschenden Domain Konten, Aliase und/oder Relocated User zugeordnet sein, wird .B vmm die Ausführung des Befehls mit einer entsprechenden Fehlermeldung beenden. Sollten Sie sich Ihres Vorhabens sicher sein, so kann optional das Schlüsselwort .B force angegeben werden. .PP Sollten Sie wirklich immer wissen was Sie tun, so editieren Sie Ihre .I vmm.cfg und setzen den Wert der Option .I domain.force_deletion auf .BR true . Dann werden Sie zukünftig beim Löschen von Domains nicht mehr wegen vorhanden Konten, Aliase und/oder Relocated User gewarnt. .\" ------------------------------------ .SS domaininfo (di) .B vmm domaininfo .IR fqdn \ [ details ] .PP Dieser Unterbefehl zeigt Informationen zur Domain mit dem angegebenen .I fqdn an. .PP Um detaillierte Informationen über die Domain zu erhalten, kann das optionale Argument .I details angegeben werden. Ein möglicher Wert für .I details kann eines der folgenden sechs Schlüsselwörter sein: .RS .TP 13 .B accounts um alle eingerichteten Konten aufzulisten .TP .B aliasdomains um alle zugeordneten Alias\-Domains aufzulisten .TP .B aliases um alle vorhandenen Alias\-Adressen aufzulisten .TP .B catchall um alle Catch\-all\-Ziele aufzulisten .TP .B relocated um alle Adressen der Relocated Users aufzulisten .TP .B full um alle oben genannten Informationen aufzulisten .RE .PP Beispiel: .PP .nf .B vmm domaininfo sales.example.com Domain Informationen -------------------- Domain Name......: sales.example.com GID..............: 70708 Domain Directory.: /srv/mail/c/70708 Quota Limit/User.: Storage: 500,00 MiB; Messages: 10.000 Active Services..: IMAP SIEVE Transport........: lmtp:unix:private/dovecot-lmtp Alias Domains....: 0 Accounts.........: 1 Aliases..........: 0 Relocated........: 0 Catch-All Dests..: 1 .fi .\" ------------------------------------ .SS domainquota (dq) .B vmm domainquota .IR "fqdn storage" " [" messages ] .RB [ force ] .PP Dieser Unterbefehl wird verwendet, um für die Konten der Domain ein neues Quota\-Limit festzulegen. .PP Standardmäßig gilt für Konten das Quota\-Limit der .IR vmm.cfg " (" domain.quota_bytes " und " domain.quota_messages ). Das neue Quota\-Limit gilt für für alle bestehenden Konten, die nicht selbst ein Quota\-Limit definieren. Soll das neue Quota\-Limit auch für Konten mit eigenen Limiten angewendet werden, so ist das optionale Schlüsselwort .B force anzugeben. .br Wenn der Wert für das Argument .I messages ausgelassen wurde, wird der Vorgabewert .B 0 (null) als Anzahl von Nachrichten angewendet werden. .PP Beispiel: .PP .nf .B vmm domainquota example.com 1g force .fi .\" ------------------------------------ .SS domainservices (ds) .B vmm domainservices .IR fqdn " [" "service ..." ] .RB [ force ] .PP Um festzulegen, welche Services für die Anwender der Domain \(em mit dem angegebenen .I fqdn \(em nutzbar sein sollen, wird dieser Unterbefehl verwendet. .PP Der Zugriff auf alle genannten Services wird den Anwender gestattet. Der Zugriff auf nicht genannte Services wird verweigert werden. Verwendbare .IR service \-Namen sind: .BR imap ", " pop3 ", " sieve " und " smtp . .br Wird das Schlüsselwort .B force angegeben, so werden alle kontospezifischen Einstellungen gelöscht und es gelten fortan die Service\-Einstellungen der Domain für alle Konten. Ohne dieses Schlüsselwort gelten die neuen Einstellungen nur für Konten, bei denen die Service\-Einstellungen nicht individuell geändert wurden. .\" ------------------------------------ .SS domaintransport (dt) .BI "vmm domaintransport" " fqdn transport" .RB [ force ] .PP Ein neuer .I transport für die Domain mit dem angegebenen .I fqdn kann mit diesem Unterbefehl festgelegt werden. .PP Wird das Schlüsselwort .B force angegeben, so werden alle kontospezifischen Einstellungen gelöscht und es gelten fortan die Transport\-Einstellungen der Domain für alle Konten. Ohne dieses Schlüsselwort gelten die neuen Einstellungen nur für Konten, bei denen die Transport\-Einstellungen nicht individuell geändert wurden. .PP Beispiel: .PP .nf .B vmm domaintransport support.example.com dovecot: .fi .\" ------------------------------------ .SS domainnote (do) .BI "vmm domainnote" " fqdn" .RI [ note ] .PP Mit diesem Unterbefehl kann eine Domain mit einer Notiz versehen werden. Um die Notiz wieder zu löschen, läßt man sie einfach weg. .PP Beispiel: .PP .nf .B vmm do example.com Gehört Robert .fi .\" ----------------------------------------------------------------------- .SH ALIAS\-DOMAIN UNTERBEFEHLE Eine Alias\-Domain ist ein Alias für eine Domain, die zuvor mit dem Unterbefehl .B domainadd erstellt wurde. Alle Konten, Aliase und Relocated Users der Domain sind ebenfalls unter der Alias\-Domain verfügbar. .br Im Folgenden wird angenommen, example.net sei ein Alias für example.com. .PP Postfix wird nicht erst fälschlicherweise E\-Mails für unbekannten.user@example.net annehmen und später an den \(em oftmals gefälschten \(em Absender bouncen. Postfix wird E\-Mails an unbekannte Empfänger sofort ablehnen. .br Dieses Verhalten ist sichergestellt, solange die empfohlenen Datenbankabfragen in .I $config_directory/pgsql\-*.cf konfiguriert sind. .\" ------------------------------------ .SS aliasdomainadd (ada) .BI "vmm aliasdomainadd" " fqdn destination" .PP Dieser Unterbefehl legt die Alias\-Domain .RI ( fqdn ) als Alias für eine bestehende Domain .RI ( destination ") an." .PP Beispiel: .PP .nf .B vmm aliasdomainadd example.net example.com .fi .\" ------------------------------------ .SS aliasdomaindelete (add) .BI "vmm aliasdomaindelete" " fqdn" .PP Verwenden Sie diesen Unterbefehl, um die Alias\-Domain .I fqdn zu löschen. .PP Beispiel: .PP .nf .B vmm aliasdomaindelete e.g.example.com .fi .\" ------------------------------------ .SS aliasdomaininfo (adi) .BI "vmm aliasdomaininfo" " fqdn" .PP Dieser Unterbefehl gibt Informationen darüber aus, welcher Domain die Alias\-Domain .I fqdn aktuell zugeordnet ist. .PP Beispiel: .PP .nf .B vmm adi example.net Alias\-Domain Informationen -------------------------- Die Alias\-Domain example.net gehört zu: * example.com .fi .\" ------------------------------------ .SS aliasdomainswitch (ads) .BI "vmm aliasdomainswitch" " fqdn destination" .PP Wenn Sie die bereits vorhandene Alias\-Domain .I fqdn einer anderen Ziel\-Domain zuordnen wollen, verwenden Sie diesen Unterbefehl. .PP Beispiel: .PP .nf .B vmm aliasdomainswitch example.net example.org .fi .\" ----------------------------------------------------------------------- .SH KONTO UNTERBEFEHLE .SS useradd (ua) .B vmm useradd .IR address " [" password ] .PP Mit diesem Unterbefehl wird ein neues Konto für die angegebene Adresse angelegt. .PP Wurde kein Passwort angegeben wird .B vmm dieses im interaktiven Modus erfragen. Falls kein Passwort angegeben wurde und .I account.random_password den Wert .B true hat, wird .B vmm ein zufälliges Passwort generieren und auf stdout ausgeben, nachdem das Konto angelegt wurde. .PP Beispiele: .PP .nf .B vmm ua d.user@example.com \(dqA 5ecR3t P4s5\(rs/\(rs/0rd\(dq .B vmm useradd e.user@example.com Neues Passwort eingeben: Neues Passwort wiederholen: .fi .\" ------------------------------------ .SS userdelete (ud) .BI "vmm userdelete" " address" .RB [ force ] .PP Verwenden Sie diesen Unterbefehl, um das Konto mit der angegebenen Adresse zu löschen. .PP Sollte es einen oder mehrere Aliase geben, deren Ziel\-Adresse mit der Adresse des zu löschenden Kontos identisch ist, wird .B vmm die Ausführung des Befehls mit einer entsprechenden Fehlermeldung beenden. Um dieses zu umgehen, kann das optionale Schlüsselwort .B force angegebenen werden. .\" ------------------------------------ .SS userinfo (ui) .B "vmm userinfo" .IR address " [" details ] .PP Dieser Unterbefehl zeigt einige Informationen über das Konto mit der angegebenen Adresse an. .PP Wurde das optionale Argument .I details angegeben, werden weitere Informationen ausgegeben. Mögliche Werte für .I details sind: .RS .TP 8 .B aliases um alle Alias\-Adressen, mit dem Ziel .IR address , aufzulisten .TP .B du um zusätzlich die Festplattenbelegung des Mail\-Verzeichnisses eines Kontos anzuzeigen. Soll die Festplattenbelegung jedes Mal mit der .B userinfo ermittelt werden, ist in der .I vmm.cfg der Wert der Option .I account.disk_usage auf .B true zu setzen. .TP .B full um alle oben genannten Informationen anzuzeigen .RE .PP Beispiel: .PP .nf .B vmm userinfo d.user@example.com Konto Informationen ------------------- Address..........: d.user@example.com Name.............: None UID..............: 79881 GID..............: 70704 Home.............: /srv/mail/2/70704/79881 Mail_Location....: mdbox:~/mdbox Quota Storage....: [ 0,00%] 0/500,00 MiB Quota Messages...: [ 0,00%] 0/10.000 Transport........: lmtp:unix:private/dovecot-lmtp SMTP.............: deaktiviert POP3.............: deaktiviert IMAP.............: aktiviert SIEVE............: aktiviert .fi .\" ------------------------------------ .SS username (un) .BI "vmm username" " address" .RI [ name ] .PP Der bürgerliche Name des Kontoinhabers mit der angegebenen Adresse kann mit diesem Unterbefehl gesetzt/aktualisiert werden. .PP Wird kein .I name übergeben, so wird der Wert in den Kontoinformationen gelöscht. .PP Beispiel: .PP .nf .B vmm username d.user@example.com \(dqJohn Doe\(dq .fi .\" ------------------------------------ .SS userpassword (up) .BI "vmm userpassword" " address" .RI [ password ] .PP Das Passwort eines Kontos kann mit diesem Unterbefehl aktualisiert werden. .PP Wurde kein Passwort angegeben, wird .B vmm dieses im interaktiven Modus erfragen. .PP Beispiel: .PP .nf .B vmm up d.user@example.com \(dqA |\(rs/|0r3 5ecur3 P4s5\(rs/\(rs/0rd?\(dq .fi .\" ------------------------------------ .SS usernote (uo) .BI "vmm usernote" " address" .RI [ note ] .PP Mit diesem Unterbefehl kann ein Konto mit einer Notiz versehen werden. Um die Notiz wieder zu löschen, läßt man sie einfach weg. .PP Beispiel: .PP .nf .B vmm uo d.user@example.com Wird nur bis Ende Mai 2012 gebraucht .fi .\" ------------------------------------ .SS userquota (uq) .BI "vmm userquota" " address storage" .RI [ messages ] .PP Um ein neues Quota\-Limit für das Konto mit der angegebenen Adresse festzulegen, wird dieser Unterbefehl verwendet. .PP Wenn der Wert für das Argument .I messages ausgelassen wurde, wird der Vorgabewert .B 0 (null) als Anzahl von Nachrichten angewendet werden. .PP Anstelle der Limits, bewirkt das Wort .BR domain , dass die Limits des Kontos gelöscht wird und somit wieder der in der Domain gespeicherte Wert für das Konto gilt. .PP Beispiel: .PP .nf .B vmm userquota d.user@example.com 750m .B vmm userquote d.user@example.com domain .fi .\" ------------------------------------ .SS userservices (us) .B vmm userservices .IR address " [" "service ..." ] .PP Verwenden Sie diesen Unterbefehl, um einem Anwender den Zugriff auf die genannten Services zu gestatten. .PP Der Zugriff auf alle nicht genannten Services wird dem Anwender, mit der angegebenen Adresse, verwehrt werden. .PP Anstelle einer Liste, bewirkt das Wort .BR domain , dass die benutzerspezifische Liste gelöscht wird und somit wieder die in der Domain gespeicherte Liste für das Konto gilt. .PP Beispiel: .PP .nf .B vmm userservices d.user@example.com SMTP IMAP .B vmm userservices d.user@example.com domain .\" ------------------------------------ .SS usertransport (ut) .BI "vmm usertransport" " address transport" .PP Mit diesem Unterbefehl kann ein abweichender .I transport für das Konto mit der angegebenen Adresse bestimmt werden. .PP Wird als .I transport das Wort 'domain' übergeben, so wird der explizite Transport des Kontos wieder gelöscht und der in der Domain gespeicherte Wert benutzt. .PP Beispiel: .br Angenommen, Sie wollen mit Dovecots .BR dsync (1) die E\-Mails vom Maildir\-Format ins mdbox\-Format konvertieren, dann können Sie Postfix, über den Transport, darüber informieren, es später nochmals zu versuchen. .PP .nf .B vmm ut d.user@example.com \(dqretry:4.0.0 Mailbox being migrated\(dq # Konvertieren der Mailbox … # … danach den Transport auf den Domainwert setzen .B vmm usertransport d.user@example.com domain .fi .\" ----------------------------------------------------------------------- .SH ALIAS UNTERBEFEHLE .SS aliasadd (aa) .BI "vmm aliasadd" " address destination ..." .PP Mit diesem Unterbefehl werden neue Alias\-Adressen, mit einer oder mehren .IR destination (en), erstellt. .PP Innerhalb der Zieladresse werden die Zeichenketten .IR %n , .IR %d und .IR %= durch den ursprünglichen lokalen Teil, die Domain bzw. die Emailadresse mit '=' anstelle von '@' ersetzt. Dies ermöglicht z.B. in Verbindung mit Alias\-Domains domain\-spezifische Empfänger. .PP Beispiele: .PP .nf .B vmm aliasadd john.doe@example.com d.user@example.com .B vmm aa support@example.com d.user@example.com e.user@example.com .B vmm aa postmaster@example.com postmaster+%d@example.org .fi .\" ------------------------------------ .SS aliasdelete (ad) .BI "vmm aliasdelete" " address" .RI [ destination " ...]" .PP Verwenden Sie diesen Unterbefehl um den Alias mit der angegebenen Adresse zu löschen. .PP Wurden eine oder mehrere optionale .I destination Adressen angegeben, so werden nur diese .IR destination s vom angegebenen Alias entfernt. .PP Beispiel: .PP .nf .B vmm aliasdelete support@example.com d.user@example.com .fi .\" ------------------------------------ .SS aliasinfo (ai) .BI "vmm aliasinfo" " address" .PP Informationen zum Alias mit der angegebenen Adresse können mit diesem Unterbefehl ausgegeben werden. .PP Beispiel: .PP .nf .B vmm aliasinfo support@example.com Alias Informationen ------------------- E\-Mails für support@example.com werden weitergeleitet an: * e.user@example.com .fi .\" ----------------------------------------------------------------------- .SH RELOCATED UNTERBEFEHLE .SS relocatedadd (ra) .BI "vmm relocatedadd" " address newaddress" .PP Um einen neuen Relocated User anzulegen kann dieser Unterbefehl verwendet werden. .PP Dabei ist .I address die ehemalige Adresse des Benutzers, zum Beispiel b.nutzer@example.com, und .I newaddress die neue Adresse, unter der die/der Benutzer/in erreichbar ist. .PP Beispiel: .PP .nf .B vmm relocatedadd b.nutzer@example.com b\-nutzer@firma.tld .fi .\" ------------------------------------ .SS relocatedinfo (ri) .BI "vmm relocatedinfo " address .PP Dieser Unterbefehl zeigt die neue Adresse des Relocated Users mit mit der angegebenen Adresse. .PP Beispiel: .PP .nf .B vmm relocatedinfo b.nutzer@example.com Verschiebe\-Informationen ------------------------ Der Benutzer »b.nutzer@example.com« wurde nach »b\-nutzer@firma.tld« verschoben .fi .\" ------------------------------------ .SS relocateddelete (rd) .BI "vmm relocateddelete " address .PP Mit diesem Unterbefehl kann der Relocated User mit der angegebenen Adresse gelöscht werden. .PP Beispiel: .PP .nf .B vmm relocateddelete b.nutzer@example.com .fi .\" ----------------------------------------------------------------------- .SH CATCH\-ALL UNTERBEFEHLE .SS catchalladd (caa) .BI "vmm catchalladd" " fqdn destination ..." .PP Mit diesem Unterbefehl können für eine Domain Adressen definiert werden, an die E\-Mails geleitet werden, die an nicht\-existente Adressen innerhalb dieser Domains adressiert sind. Diese Adressen \(dqfangen alle\(dq diese E\-Mails auf, es sei denn es bestehen spezifischere Aliase, Mailboxen oder Relocated\-Einträge. .PP WARNUNG: Catch\-all Adressen können dazu führen, dass ein Mailserver von Spam überflutet wird, da Spammer zuweilen gerne alle möglichen Emailadressen ausprobieren und man auf einmal zig tausend Nachrichten gerichtet an Adressen von abba@example.org bis zztop@example.org weitergeleitet bekommt. .PP Beispiel: .PP .nf .B vmm catchalladd example.com b.nutzer@example.org .fi .\" ------------------------------------ .SS catchallinfo (cai) .BI "vmm catchallinfo " fqdn .PP Dieser Unterbefehl zeigt die für eine Domain definierten Catch\-all Aliase an. .PP Beispiel: .PP .nf .B vmm catchallinfo example.com Catch-all Informationen ----------------------- Nachrichten an unbekannte Adressen innerhalb der example.com Domäne werden weitergeleitet an: * b.nutzer@example.org .fi .\" ------------------------------------ .SS catchalldelete (cad) .BI "vmm catchalldelete " fqdn .RI [ destination " ...]" .PP Mit diesem Unterbefehl werden Catch\-all Aliase einer Domain wieder gelöscht, entweder nur das/die angegebene(n) Alias(e), oder alle, wenn keine .I destination Adresse angegeben wurde. .PP Beispiel: .PP .nf .B vmm catchalldelete example.com b.nutzer@example.org .fi .\" ----------------------------------------------------------------------- .SH DATEIEN .TP .I /root/vmm.cfg Wird verwendet, falls vorhanden. .TP .I /usr/local/etc/vmm.cfg Wird verwendet, sollte obige Datei nicht gefunden werden. .TP .I /etc/vmm.cfg Wird verwendet, falls die oben genannten Dateien nicht existieren. .\" ----------------------------------------------------------------------- .SH SIEHE AUCH .BR dsync (1), .BR transport (5), .BR vmm.cfg (5) .\" ----------------------------------------------------------------------- .SH INTERNET RESSOURCEN .TP Homepage http://vmm.localdomain.org/ .TP Projekt\-Seite http://sf.net/projects/vmm/ .TP Bugtracker https://bitbucket.org/pvo/vmm/issues .\" ----------------------------------------------------------------------- .SH COPYING vmm und die dazugehörigen Manualseiten wurden von Pascal Volk geschrieben und sind unter den Bedingungen der BSD Lizenz lizenziert.vmm-0.6.1/man/de/man5/0000755000175000017500000000000012033032424012503 5ustar pvopvovmm-0.6.1/man/de/man5/vmm.cfg.50000644000175000017500000004075012033032424014134 0ustar pvopvo.TH "VMM.CFG" "5" "2012-08-12" "vmm 0.6" "vmm" .SH NAME vmm.cfg \- Konfigurationsdatei für vmm .\" ----------------------------------------------------------------------- .SH ÜBERSICHT vmm.cfg .\" ----------------------------------------------------------------------- .SH BESCHREIBUNG .BR vmm (1) liest seine Konfigurationsparameter aus der Datei .IR vmm.cfg . .PP Die Konfigurationsdatei ist in mehrere Sektionen unterteilt. Jede Sektion wird mit dem in eckigen Klammern .RB ' [ "' und '" ] ' eingefassten Namen der Sektion eingeleitet, gefolgt von .RI ' Option " = " Wert ' Einträgen. .PP Leerräume um das Gleichheitszeichen '=' und am Ende eines Wertes werden ignoriert. Leerzeilen und Zeilen, die mit einer '#' oder einem ';' anfangen, werden ignoriert. .PP Jeder Wert ist von einem der folgenden Datentypen: .PP .TP 8 .I Boolean um festzulegen, ob etwas eingeschaltet/aktiviert (true) oder ausgeschaltet/deaktiviert (false) ist. .br Mögliche Werte für .I true sind: .BR 1 , " yes" , " true" " und " on . .br Mögliche Werte für .I false sind: .BR 0 , " no" , " false" " und " off . .TP .I Int eine Integer\-Zahl, geschrieben ohne eine gebrochene oder dezimale Komponente. .br Beispielsweise .BR 1 , " 50" " oder " 321 sind Integer\-Zahlen. .TP .I String eine Folge von Buchstaben und Zahlen. .br Zum Beispiel: .RB ' Wort "', '" "Hallo Welt" "' oder '" /usr/bin/strings ' .PP Die meisten Optionen haben einen Vorgabewert. Dieser ist nach dem Namen der Option in Klammern angegebenen. Um den Vorgabewert einer Option zu verwenden, wird die entsprechende Zeile entweder mit .BR # " oder " ; auskommentiert oder die Zeile wird einfach aus der .I vmm.cfg entfernt. .PP Eine minimale .I vmm.cfg könnte so aussehen: .PP .nf [database] user = ich pass = xxxxxxxx [misc] dovecot_version = 1.2.16 .fi .\" ----------------------------------------------------------------------- .SH SUCHREIHENFOLGE Standardmäßig sucht .BR vmm (1) die .I vmm.cfg in folgenden Verzeichnissen, in der angegebenen Reihenfolge: .RS .PD 0 .TP .I /root .TP .I /usr/local/etc .TP .I /etc .PD .RE .PP Die zuerst gefundene Datei wird verwendet. .\" ----------------------------------------------------------------------- .SH SEKTION ACCOUNT Die Optionen der Sektion .B account legen Konto\-spezifische Einstellungen fest. .SS account.delete_directory .BR delete_directory " (Vorgabe: false) :" .I Boolean .PP Bestimmt das Verhalten von .BR vmm (1) beim Löschen eines Kontos (userdelete). Wenn der Wert dieser Option .I true ist, wird das Home\-Verzeichnis des zu löschenden Anwenders rekursiv gelöscht. .\" ------------------------------------ .SS account.directory_mode .BR directory_mode " (Vorgabe: 448) :" .I Int .PP Zugriffsbits des Home\-Verzeichnisses, sowie aller enthaltenen Verzeichnisse, in Dezimal\-Schreibweise (Basis 10). .br Beispiel: 'drwx\-\-\-\-\-\-' \(-> oktal 0700 \(-> dezimal 448 .\" ------------------------------------ .SS account.disk_usage .BR disk_usage " (Vorgabe: false) :" .I Boolean .PP Legt fest, ob die Festplattenbelegung des Maildirs eines Benutzers jedes Mal mit .BR du (1) ermittelt und mit den Konto\-Informationen ausgegeben werden soll. .PP Bei umfangreichen Maildirs kann das langsam sein. Falls Sie Quotas aktiviert haben, wird der .BR vmm\-Unterbefehl userinfo ebenfalls die aktuelle Quota\-Nutzung des Kontos mit ausgegeben. Sie können auch eines der optionalen Argumente .BR du " oder " full an userinfo übergeben, um sich die aktuelle Festplattenbelegung anzeigen zu lassen. .\" ------------------------------------ .SS account.password_length .BR password_length " (Vorgabe: 8) :" .I Int .PP Diese Option legt die Anzahl der Zeichen für automatisch erzeugte Passwörter fest. Alle Werte kleiner als 8 werden auf 8 erhöht. .\" ------------------------------------ .SS account.random_password .BR random_password " (Vorgabe: false) :" .I Boolean .PP Mit dieser Option wird bestimmt, ob .BR vmm (1) ein zufälliges Passwort generieren soll, wenn kein Passwort an den Unterbefehl useradd übergeben wurde. Ist der Wert dieser Option .IR false , wird .B vmm Sie auffordern, ein Passwort für den neuen Account einzugeben. .PP Sie können die Länge für automatisch generierte Passwörter mit der Option .I account.password_length konfigurieren. .\" ----------------------------------------------------------------------- .SH SEKTION BIN In der .BR bin \-Sektion werden die Pfade zu den von .BR vmm (1) benötigten Binaries angegeben. .SS bin.dovecotpw .BR dovecotpw " (Vorgabe: /usr/sbin/dovecotpw) :" .I String .PP Der absolute Pfad zum dovecotpw Binary. Geben Sie den absoluten Pfad zum .BR doveadm (1) Binary an, falls Sie Dovecot v2.0 verwenden. .PP Dieses Binary wird zur Hash\-Erzeugung verwendet, wenn .I misc.password_scheme einen der nachfolgenden Werte hat: 'CRAM\-MD5', 'HMAC\-MD5', 'LANMAN', \(aqOTP', 'RPA' oder 'SKEY'. Dieses Binary wird auch benötigt, wenn Ihre Python\-Installation einen der folgenden Hash\-Algorithmen nicht unterstützt: .IP \(bu 4 md4: (hashlib + OpenSSL oder PyCrypto) verwendet für die Passwort\-Schemen: \(aqPLAIN\-MD4' und 'NTLM' .IP \(bu sha256: (hashlib oder PyCrypto \(>= 2.1.0alpha1) verwendet für die Passwort\-Schemen: 'SHA256' und 'SSHA256' .IP \(bu sha512: (hashlib) verwendet für die Passwort\-Schemen: 'SHA512' und \(aqSSHA512' .PP Das .BR doveadm (1) Binary wird auch gebraucht, um die INBOX und zusätzliche Mailboxen .RI ( mailbox.folders ) für einen neuen Account zu erstellen, wenn die Option .I mailbox.format den Wert .BR mdbox " oder " sdbox hat. .\" ------------------------------------ .SS bin.du .BR du " (Vorgabe: /usr/bin/du) :" .I String .PP Der absolute Pfad zu .BR du (1). Dieses Binary wird verwendet, wenn die Festplattenbelegung eines Kontos ermittelt wird. .\" ------------------------------------ .SS bin.postconf .BR postconf " (Vorgabe: /usr/sbin/postconf) :" .I String .PP Der absolute Pfad zu Postfix' .BR postconf (1). Dieses Binary wird verwendet, wenn .BR vmm (1) diverse Postfix\-Einstellungen prüft, zum Beispiel das .IR virtual_alias_expansion_limit . .\" ----------------------------------------------------------------------- .SH SEKTION DATABASE Die .BR database \-Sektion wird verwendet, um die für den Datenbankzugriff erforderlichen Optionen festzulegen. .SS database.host .BR host " (Vorgabe: localhost) :" .I String .PP Der Hostname oder die IP\-Adresse des Datenbankservers. .\" ------------------------------------ .SS database.module .BR module " (Vorgabe: psycopg2) :" .I String .PP Das für den Datenbankzugriff zu verwendende Python PostgreSQL Adapter Modul. Unterstützte Module sind .BR psycopg2 " und " pyPgSQL . .\" ------------------------------------ .SS database.name .BR name " (Vorgabe: mailsys) :" .I String .PP Der Name der zu verwendenden Datenbank. .\" ------------------------------------ .SS database.pass .BR pass " (Vorgabe: " None ") :" .I String .PP Das Passwort des Datenbank\-Benutzers. .\" ------------------------------------ .SS database.port .BR port " (Vorgabe: 5432) :" .I Int .PP Der TCP\-Port, auf dem der Datenbankserver Verbindungen annimmt. .\" ------------------------------------ .SS database.sslmode .BR sslmode " (Vorgabe: prefer) :" .I String .PP Bestimmt, ob und mit welcher Priorität eine SSL\-Verbindung mit dem Datenbankserver ausgehandelt wird. Mögliche Werte sind: .BR disabled ", " allow ", " prefer ", " require ", " verify\-ca " und " .BR verify\-full . Die Modi .BR verify\-ca " und " verify\-full stehen seit PostgreSQL 8.4 zur Verfügung. .PP Diese Option wird ignoriert, wenn das .I database.module .B pyPgSQL verwendet wird. .\" ------------------------------------ .SS database.user .BR user " (Vorgabe: " None ") :" .I String .PP Der Name des Datenbank\-Benutzers. .\" ----------------------------------------------------------------------- .SH SEKTION DOMAIN In der .BR domain \-Sektion werden Domain\-spezifische Einstellungen hinterlegt. .PP Das Quota\-Limit (quota_bytes und quota_messages), Service\-Einstellungen (imap, pop3, sieve und smtp) und der Transport werden angewendet, wenn eine Domain angelegt wird. Um die Einstellungen einer vorhandenen Domain zu ändern, verwenden Sie einen der folgenden .BR vmm (1) Unterbefehle: .PP .TP .B domainquota um das Quota\-Limit einer Domain zu ändern .TP .B domainservices um einer Domain einen abweichenden Satz von nutzbaren Services zuzuweisen .TP .B domaintransport um einen neuen Vorgabe\-Transport für eine Domain festzulegen .PP Wenn ein Account angelegt wird, erbt er alle Einstellungen von der Domain, zu der er hinzugefügt wird. Abweichende Einstellungen für einen vorhandenen Account nehmen Sie mit einem der Unterbefehle .BR userquota ", " userservices " und " usertransport vor. .\" ------------------------------------ .SS domain.auto_postmaster .BR auto_postmaster " (Vorgabe: true) :" .I Boolean .PP Ist der Wert dieser Option .IR true , wird .BR vmm (1) beim Anlegen einer Domain (domainadd) automatisch einen postmaster\-Account erstellen. .\" ------------------------------------ .SS domain.delete_directory .BR delete_directory " (Vorgabe: false) :" .I Boolean .PP Legt fest, ob beim Löschen einer Domain (domaindelete) das Verzeichnis der zu löschenden Domain, inklusive aller Anwender\-Verzeichnisse, rekursiv gelöscht werden soll. .\" ------------------------------------ .SS domain.directory_mode .BR directory_mode " (Vorgabe: 504) :" .I Int .PP Zugriffsbits des Domain\-Verzeichnisses in Dezimal\-Schreibweise (Basis 10). .br Beispiel: 'drwxrwx\-\-\-' \(-> oktal 0770 \(-> dezimal 504 .\" ------------------------------------ .SS domain.force_deletion .BR force_deletion " (Vorgabe: false) :" .I Boolean .PP Erzwingt das Löschen aller zugeordneten Konten und Aliase beim Löschen einer Domain (domaindelete). .\" ------------------------------------ .SS domain.imap .BR imap " (Vorgabe: true) :" .I Boolean .PP Legt fest, ob sich neu angelegte Benutzer per IMAP anmelden können sollen. .\" ------------------------------------ .SS domain.pop3 .BR pop3 " (Vorgabe: true) :" .I Boolean .PP Legt fest, ob sich neu angelegte Benutzer per POP3 anmelden können sollen. .\" ------------------------------------ .SS domain.quota_bytes .BR quota_bytes " (Vorgabe: 0) :" .I String .PP Quota Limit in Bytes. 0 bedeutet unbegrenzt. Dieses Limit wird beim Anlegen von Domains angewendet. .PP Der Wert dieser Option kann als Integer\-Wert, zum Beispiel .B 20480 geschrieben werden. Es ist auch möglich dem Wert eines der folgenden Suffixe anzuhängen: .BR b " (Bytes), " k " (Kilobytes), " M " (Megabytes) oder " G (Gigabytes). .br 1024 entspricht 1024b oder 1k. .\" ------------------------------------ .SS domain.quota_messages .BR quota_messages " (Vorgabe: 0) :" .I Int .PP Quota Limit als Anzahl von Nachrichten. 0 bedeutet unbegrenzt. Dieses Limit wird beim Anlegen neuer Domains angewendet. .\" ------------------------------------ .SS domain.sieve .BR sieve " (Vorgabe: true) :" .I Boolean .PP Legt fest, ob sich neu angelegte Benutzer per SIEVE (ManageSieve) anmelden können sollen. .\" ------------------------------------ .SS domain.smtp .BR smtp " (Vorgabe: true) :" .I Boolean .PP Legt fest, ob sich neu angelegte Benutzer per SMTP (SMTP AUTH) anmelden können sollen. .\" ------------------------------------ .SS domain.transport .BR transport " (Vorgabe: dovecot:) :" .I String .PP Der Standard\-Transport aller neuen Domains. Siehe auch: .BR transport (5). .\" ----------------------------------------------------------------------- .SH SEKTION MAILBOX In der .BR mailbox \-Sektion werden die für die Erstellung von Mailboxen erforderlichen Optionen festgelegt. Die INBOX wird in jedem Fall erstellt. .SS mailbox.folders .BR folders " (Vorgabe: Drafts:Sent:Templates:Trash) :" .I String .PP Eine durch Doppelpunkte getrennte Liste mit Namen der zu erstellenden Mailboxen. Sollen keine zusätzlichen Mailboxen angelegt werden, ist dieser Option ein einzelner Doppelpunkt .RB (' : ') als Wert zuzuweisen. .PP Sollen Verzeichnisse mit Unterverzeichnissen angelegt werden, ist ein einzelner Punkt .RB (' . ') als Separator zu verwenden. .PP Sollen Mailboxen mit internationalisierten Namen erstellt werden (zum Beispiel: 'Wysłane' oder 'Gelöschte Objekte'), ist der Name UTF\-8 kodiert anzugeben. .BR vmm (1) wird die internationalisierten Mailboxnamen in eine modifizierten Variante des UTF\-7\-Zeichensatzes (siehe auch: RFC 3501, Sektion 5.1.3) konvertieren. .\" ------------------------------------ .SS mailbox.format .BR format " (Vorgabe: maildir) :" .I String .PP Das zu verwendende Mailbox\-Format für die Mailboxen der Benutzer. Abhängig von der verwendeten Dovecot\-Version .RI ( misc.dovecot_version ), unterstützt .BR vmm (1) bis zu drei Formate: .TP 8 .B maildir Dovecot \(>= v1.0.0 .TP .B mdbox Dovecot \(>= v2.0.beta5 .TP .B sdbox Dovecot \(>= v2.0.rc3 .\" ------------------------------------ .SS mailbox.root .BR root " (Vorgabe: Maildir) :" .I String .PP Name des Mailbox\-Wurzelverzeichnisses im Home\-Verzeichnis des jeweiligen Benutzers. Übliche Namen, je nach verwendetem .IR mailbox.format , sind .BR Maildir ", " mdbox " or " sdbox . .\" ------------------------------------ .SS mailbox.subscribe .BR subscribe " (Vorgabe: true) :" .I Boolean .PP Wenn dieser Option der Wert .B true zugewiesen wurde, werden die, gemäß .IR mailbox.folders , erstellen Mailboxen in der subscriptions\-Datei des Benutzers gelistet. Sollen die erstellen Mailboxen nicht nicht in der subscriptions\-Datei gelistet werden, weisen Sie dieser Option den Wert .B false zu. .\" ----------------------------------------------------------------------- .SH SEKTION MISC In der .BR misc \-Sektion werden Einstellungen für verschiedene Bereiche festgelegt. .SS misc.base_directory .BR base_directory " (Vorgabe: /srv/mail) :" .I String .PP Alle Domain\-Verzeichnisse werden innerhalb dieses Basis\-Verzeichnisses angelegt. .\" ------------------------------------ .SS misc.crypt_blowfish_rounds .BR crypt_blowfish_rounds " (Vorgabe: 5) :" .I Int .PP Anzahl der Verschlüsselungsdurchgänge für das .I password_scheme .BR BLF\-CRYPT . .PP Der Wert muss im Bereich von .BR 4 " \- " 31 liegen. .\" ------------------------------------ .SS misc.crypt_sha256_rounds .BR crypt_sha256_rounds " (Vorgabe: 5000) :" .I Int .PP Anzahl der Verschlüsselungdurchgänge für das .I password_scheme .BR SHA256\-CRYPT . .PP Der Wert muss im Bereich von .BR 1000 " \- " 999999999 liegen. .\" ------------------------------------ .SS misc.crypt_sha512_rounds .BR crypt_sha512_rounds " (Vorgabe: 5000) :" .I Int .PP Anzahl der Verschlüsselungdurchgänge für das .I password_scheme .BR SHA512\-CRYPT . .PP Der Wert muss im Bereich von .BR 1000 " \- " 999999999 liegen. .\" ------------------------------------ .SS misc.dovecot_version .BR dovecot_version " (Vorgabe: " None ") :" .I String .PP Die aktuell eingesetzte Dovecot\-Version. (siehe: .BR "dovecot \-\-version" ). Wenn das Kommando .B dovecot \-\-version zum Beispiel .I 2.0.beta4 (8818db00d347) ausgibt, ist dieser Option der Wert .B 2.0.beta4 zuzuweisen. .\" ------------------------------------ .SS misc.password_scheme .BR password_scheme " (Vorgabe: CRAM\-MD5) :" .I String .PP Das zu verwendende Passwort\-Schema. Um eine Liste aller verwendbaren Passwort\-Schemen zu erhalten, führen Sie das Kommando .B vmm lp aus. .PP Seit Dovecot \(>= v1.1.alpha1 ist es möglich, dem .I password_scheme ein Encoding\-Suffix anzufügen. Unterstützte Encoding\-Suffixe: .BR .b64 ", " .base64 " und " .hex . Beispiel: PLAIN.BASE64 .\" ----------------------------------------------------------------------- .SH BEISPIEL Eine Beispiel\-Konfiguration. Alle Optionen, die nicht in der Konfigurationsdatei gelistet sind, haben ihren Vorgabewert. .PP .nf [account] password_length = 10 random_password = true [bin] dovecotpw = /usr/bin/doveadm [database] host = dbsrv8.example.net pass = PY_SRJ}L/0p\-oOk port = 5433 sslmode = require user = vmm [domain] quota_bytes = 500M quota_messages = 10000 transport = lmtp:unix:private/dovecot\-lmtp [mailbox] folders = Drafts:Sent:Templates:Trash:Lists.Dovecot:Lists.Postfix [misc] crypt_sha512_rounds = 10000 dovecot_version = 2.0.beta4 password_scheme = SHA512\-CRYPT.hex .fi .\" ----------------------------------------------------------------------- .SH SIEHE AUCH .BR postconf (1), .BR vmm (1), .BR transport (5) .\" ----------------------------------------------------------------------- .SH INTERNET RESSOURCEN .TP Homepage http://vmm.localdomain.org/ .TP Projekt\-Seite http://sf.net/projects/vmm/ .TP Bugtracker https://bitbucket.org/pvo/vmm/issues .\" ----------------------------------------------------------------------- .SH COPYING vmm und die dazugehörigen Manualseiten wurden von Pascal Volk geschrieben und sind unter den Bedingungen der BSD Lizenz lizenziert.vmm-0.6.1/man/man1/0000755000175000017500000000000012033032424012107 5ustar pvopvovmm-0.6.1/man/man1/vmm.10000644000175000017500000006355112033032424013002 0ustar pvopvo.TH "VMM" "1" "2012-09-27" "vmm 0.6" "vmm" .SH NAME vmm \- command line tool to manage email domains/accounts/aliases .\" ----------------------------------------------------------------------- .SH SYNOPSIS .B vmm .IR subcommand " [" "argument ..." ] .\" ----------------------------------------------------------------------- .SH DESCRIPTION .B vmm (a virtual mail manager) is the easy to use command line tool for administrators and postmasters to manage (alias) domains, accounts, aliases and relocated users. It allows you to simply and quickly administer your mail server. .br It's designed for Dovecot and Postfix with a PostgreSQL backend. .PP Each .I subcommand has both a long and a short form. The short form is shown enclosed in parentheses. Both forms are case sensitive. .PP Most of the .IR subcommand s take one or more .IR argument s. .\" ----------------------------------------------------------------------- .SH ARGUMENTS .TP 12 .I address The complete e\-mail address .RI ( local\-part @ fqdn ) of an user account, alias address or relocated user. .\" -------------------------- .TP .I destination Is either an e\-mail .I address when used with .IR "ALIAS SUBCOMMANDS" . Or a .I fqdn when used with .IR "ALIASDOMAIN SUBCOMMANDS" . .\" -------------------------- .TP .I fqdn The fully qualified domain name \- without the trailing dot \- of a domain or alias domain. .\" -------------------------- .TP .I messages An integer value which specifies a quota limit in number of messages. .B 0 (zero) means unlimited \- no quota limit for the number of messages. .\" -------------------------- .TP .I option is the name of a configuration option, prefixed with the section name and a dot. For example: .IB misc . transport .br All configuration options are described in .BR vmm.cfg (5). .\" -------------------------- .TP .I service The name of a service, commonly used with Dovecot. Supported services are: .BR imap ", " pop3 ", " sieve " and " smtp . .\" -------------------------- .TP .I storage Specifies a quota limit in bytes. One of the following prefixes can be appended to the integer value: .BR b " (bytes), " k " (kilobytes), " M " (megabytes) or " G (gigabytes). .B 0 (zero) means unlimited \- no quota limit in bytes. .\" -------------------------- .TP .I transport A transport for Postfix, written as: .IB transport : or .IB transport :\c .IR nexthop . See .BR transport (5) for more details. .\" ----------------------------------------------------------------------- .SH GENERAL SUBCOMMANDS .SS configget (cg) .BI "vmm configget" " option" .PP This subcommand is used to display the actual value of the given configuration .IR option . .PP Example: .PP .nf .B vmm configget misc.crypt_sha512_rounds misc.crypt_sha512_rounds = 5000 .fi .\" ------------------------------------ .SS configset (cs) .B vmm configset .I option value .PP Use this subcommand to set or update a single configuration option's value. .I option is the configuration option, .I value is the .IR option 's new value. .IP Note: This subcommand will create a new .I vmm.cfg without any comments. Your current configuration file will be backed as .IR vmm.cfg.bak . .PP Example: .PP .nf .B vmm configget domain.transport domain.transport = dovecot: .B vmm configset domain.transport lmtp:unix:private/dovecot\-lmtp .B vmm cg domain.transport domain.transport = lmtp:unix:private/dovecot\-lmtp .fi .\" ------------------------------------ .SS configure (cf) .B vmm configure .RI [ section ] .PP Starts the interactive configuration for all configuration sections. .PP In this process the currently set value of each option will be displayed in square brackets. If no value is configured, the default value of each option will be displayed in square brackets. Press the return key, to accept the displayed value. .PP If the optional argument .I section is given, only the configuration options from the given section will be displayed and will be configurable. The following sections are available: .RS .TP 10 .B account Account settings .TP .B bin Paths to external binaries .TP .B database Database settings .TP .B domain Domain settings .TP .B mailbox Mailbox settings .TP .B misc Miscellaneous settings .RE .PP All configuration options are described in .BR vmm.cfg (5). .IP Note: This subcommand will create a new .I vmm.cfg without any comments. Your current configuration file will be backed as .IR vmm.cfg.bak . .PP Example: .PP .nf .B vmm configure mailbox Using configuration file: /usr/local/etc/vmm.cfg * Configuration section: `mailbox' Enter new value for option folders [Drafts:Sent:Templates:Trash]: Enter new value for option format [maildir]: mdbox Enter new value for option subscribe [True]: Enter new value for option root [Maildir]: mdbox .fi .\" ------------------------------------ .SS getuser (gu) .BI "vmm getuser" " uid" .PP If only the .I uid is available, for example from process list, the subcommand .B getuser will show the user's address. .PP Example: .PP .nf .B vmm getuser 79876 Account information ------------------- UID............: 79876 GID............: 70704 Address........: a.user@example.com .fi .\" ------------------------------------ .SS help (h) .B vmm help .RI [ subcommand ] .PP Prints a list of available subcommands with a short description to stdout. When a .I subcommand was given, help for that .I subcommand will be displayed. After this .B vmm exits. .\" ------------------------------------ .SS listdomains (ld) .B vmm listdomains .RI [ pattern ] .PP This subcommand lists all available domains. All domain names will be prefixed either with `[+]', if the domain is a primary domain, or with `[-]', if it is an alias domain name. The output can be limited with an optional .IR pattern . .PP To perform a wild card search, the % character can be used at the start and/or the end of the .IR pattern . .PP Example: .PP .nf .B vmm listdomains %example% Matching domains ---------------- [+] example.com [\-] e.g.example.com [\-] example.name [+] example.net [+] example.org .fi .\" ------------------------------------ .SS listaddresses (ll) .B vmm listaddresses .RI [ pattern ] .PP This command lists all defined addresses. Addresses belonging to alias-domains are prefixed with a '-', addresses of regular domains with a '+'. Additionally, the letters 'u', 'a', and 'r' indicate the type of each address: user, alias and relocated respectively. The output can be limited with an optional .IR pattern . .PP To perform a wild card search, the % character can be used at the start and/or the end of the .IR pattern . .PP Example: .PP .nf .B vmm listaddresses example.com .B vmm listaddresses %master@% .\" ------------------------------------ .SS listaliases (la) .B vmm listaliases .RI [ pattern ] .PP This command lists all defined aliases. Aliases belonging to alias-domains are prefixed with a '-', addresses of regular domains with a '+'. The output can be limited with an optional .IR pattern . .PP To perform a wild card search, the % character can be used at the start and/or the end of the .IR pattern . .PP Example: .PP .nf .B vmm listaliases example.com .B vmm listaliases %master@% .\" ------------------------------------ .SS listrelocated (lr) .B vmm listrelocated .RI [ pattern ] .PP This command lists all defined relocated addresses. Relocated entries belonging to alias-domains are prefixed with a '-', addresses of regular domains with a '+'. The output can be limited with an optional .IR pattern . .PP To perform a wild card search, the % character can be used at the start and/or the end of the .IR pattern . .PP Example: .PP .nf .B vmm listrelocated example.com .B vmm listrelocated %master@% .\" ------------------------------------ .SS listusers (lu) .B vmm listusers .RI [ pattern ] .PP This command lists all user accounts. User accounts belonging to alias-domains are prefixed with a '-', addresses of regular domains with a '+'. The output can be limited with an optional .IR pattern . .PP To perform a wild card search, the % character can be used at the start and/or the end of the .IR pattern . .PP Example: .PP .nf .B vmm listusers example.com .B vmm listusers %master@% .\" ------------------------------------ .SS listpwschemes (lp) .B vmm listpwschemes .PP This subcommand lists all password schemes which could be used in the .I vmm.cfg as value of the .I misc.password_scheme option. The output varies, depending on the used Dovecot version and the system's libc. .br When your Dovecot installation isn't too old, you will see additionally a few usable encoding suffixes. One of them can be appended to the password scheme. .PP Example: .PP .nf .B vmm listpwschemes Usable password schemes ----------------------- CRYPT SHA512-CRYPT LDAP-MD5 DIGEST-MD5 SHA256 SHA512 SSHA512 SKEY SSHA NTLM RPA MD5-CRYPT HMAC-MD5 SHA1 PLAIN SHA CRAM-MD5 SSHA256 MD5 LANMAN CLEARTEXT PLAIN-MD5 PLAIN-MD4 OTP SMD5 SHA256-CRYPT Usable encoding suffixes ------------------------ .B64 .BASE64 .HEX .fi .\" ------------------------------------ .SS version (v) .B vmm version .PP Prints .BR vmm 's version and copyright information to stdout. After this .B vmm exits. .\" ----------------------------------------------------------------------- .SH DOMAIN SUBCOMMANDS .SS domainadd (da) .B vmm domainadd .IR fqdn " [" transport ] .PP Adds the new domain into the database and creates the domain directory. .PP If the optional argument .I transport is given, it will override the default transport .RI ( domain.transport ") from " vmm.cfg . The specified .I transport will be the default transport for all new accounts in this domain. .PP Configuration\-related behavior: .RS .TP .I domain.auto_postmaster When that option is set to .BR true " (default) " vmm will automatically create the postmaster account for the new domain and prompt for .BI postmaster@ fqdn\c \(aqs password. .TP .I account.random_password When the value of that option is also set to .BR true ", " vmm will automatically create the postmaster account for the new domain and print the generated postmaster password to stdout. .RE .PP Examples: .PP .nf .B vmm domainadd support.example.com smtp:[mx1.example.com]:2025 Creating account for postmaster@support.example.com Enter new password: Retype new password: .B vmm cs account.random_password true .B vmm domainadd sales.example.com Creating account for postmaster@sales.example.com Generated password: pLJUQ6Xg_z .fi .\" ------------------------------------ .SS domaindelete (dd) .BI "vmm domaindelete " fqdn .RB [ force ] .PP This subcommand deletes the domain specified by .IR fqdn . .PP If there are accounts, aliases and/or relocated users assigned to the given domain, .B vmm will abort the requested operation and show an error message. If you know, what you are doing, you can specify the optional keyword .BR force . .PP If you really always know what you are doing, edit your .I vmm.cfg and set the option .I domain.force_deletion to .BR true . .\" ------------------------------------ .SS domaininfo (di) .B vmm domaininfo .IR fqdn \ [ details ] .PP This subcommand shows some information about the given domain. .PP For a more detailed information about the domain the optional argument .I details can be specified. A possible .I details value can be one of the following six keywords: .RS .TP 14 .B accounts to list the e\-mail addresses of all existing user accounts .TP .B aliasdomains to list all assigned alias domain names .TP .B aliases to list all available alias e\-mail addresses .TP .B catchall to list all catch\-all destinations .TP .B relocated to list the e\-mail addresses of all relocated users .TP .B full to list all information mentioned above .RE .PP Example: .PP .nf .B vmm domaininfo sales.example.com Domain information ------------------ Domain Name......: sales.example.com GID..............: 70708 Domain Directory.: /srv/mail/c/70708 Quota Limit/User.: Storage: 500.00 MiB; Messages: 10,000 Active Services..: IMAP SIEVE Transport........: lmtp:unix:private/dovecot-lmtp Alias Domains....: 0 Accounts.........: 1 Aliases..........: 0 Relocated........: 0 Catch-All Dests..: 1 .fi .\" ------------------------------------ .SS domainquota (dq) .B vmm domainquota .IR "fqdn storage" " [" messages ] .RB [ force ] .PP This subcommand is used to configure a new quota limit for the accounts of the domain - not for the domain itself. .PP The default quota limit for accounts is defined in the .IR vmm.cfg " (" domain.quota_bytes " and " domain.quota_messages ). .PP The new quota limit will affect only those accounts for which the limit has not been overridden. If you want to restore the default to all accounts, you may pass the keyword .BR force . .br When the argument .I messages was omitted the default number of messages .B 0 (zero) will be applied. .PP Example: .PP .nf .B vmm domainquota example.com 1g force .fi .\" ------------------------------------ .SS domainservices (ds) .B vmm domainservices .IR fqdn " [" "service ..." ] .RB [ force ] .PP To define which services could be used by the users of the domain \(em with the given .I fqdn \(em use this subcommand. .PP Each specified .I service will be enabled/usable. All other services will be deactivated/unusable. Possible service names are: .BR imap ", " pop3 ", " sieve " and " smtp . .br The new service set will affect only those accounts for which the set has not been overridden. If you want to restore the default to all accounts, you may pass the keyword .BR force . .\" ------------------------------------ .SS domaintransport (dt) .BI "vmm domaintransport" " fqdn transport" .RB [ force ] .PP A new transport for the indicated domain can be set with this subcommand. .PP The new transport will affect only those accounts for which the transport has not been overridden. If you want to restore the default to all accounts, you may pass the keyword .BR force . .PP Example: .PP .nf .B vmm domaintransport support.example.com dovecot: .fi .\" ------------------------------------ .SS domainnote (do) .BI "vmm domainnote" " fqdn" .RI [ note ] .PP With this subcommand, it is possible to attach a note to the specified domain. Without an argument, an existing note is removed. .PP Example: .PP .nf .B vmm do example.com Belongs to Robert .fi .\" ----------------------------------------------------------------------- .SH ALIAS DOMAIN SUBCOMMANDS An alias domain is an alias for a domain that was previously added with the subcommand .BR domainadd . All accounts, aliases and relocated users from the domain will be also available in the alias domain. .br In the following is to be assumed that example.net is an alias for example.com. .PP Postfix will not accept erroneously e\-mails for unknown.user@example.net and bounce them back later to the mostly faked sender. Postfix will immediately reject all e\-mails addressed to nonexistent users. .br This behavior is ensured as long as you use the recommended database queries in your .I $config_directory/pgsql\-*.cf configuration files. .\" ------------------------------------ .SS aliasdomainadd (ada) .BI "vmm aliasdomainadd" " fqdn destination" .PP This subcommand adds the new alias domain .RI ( fqdn ) to the .I destination domain that should be aliased. .PP Example: .PP .nf .B vmm aliasdomainadd example.net example.com .fi .\" ------------------------------------ .SS aliasdomaindelete (add) .BI "vmm aliasdomaindelete" " fqdn" .PP Use this subcommand if the alias domain .I fqdn should be removed. .PP Example: .PP .nf .B vmm aliasdomaindelete e.g.example.com .fi .\" ------------------------------------ .SS aliasdomaininfo (adi) .BI "vmm aliasdomaininfo" " fqdn" .PP This subcommand shows to which domain the alias domain .I fqdn is assigned to. .PP Example: .PP .nf .B vmm adi example.net Alias domain information ------------------------ The alias domain example.net belongs to: * example.com .fi .\" ------------------------------------ .SS aliasdomainswitch (ads) .BI "vmm aliasdomainswitch" " fqdn destination" .PP If the destination of the existing alias domain .I fqdn should be switched to another .I destination use this subcommand. .nf .PP Example: .PP .B vmm aliasdomainswitch example.name example.org .fi .\" ----------------------------------------------------------------------- .SH ACCOUNT SUBCOMMANDS .SS useradd (ua) .B vmm useradd .IR address " [" password ] .PP Use this subcommand to create a new e\-mail account for the given .IR address . .PP If the .I password is not provided, .B vmm will prompt for it interactively. When no .I password is provided and .I account.random_password is set to .BR true ", " vmm will generate a random password and print it to stdout after the account has been created. .PP Examples: .PP .nf .B vmm ua d.user@example.com \(dqA 5ecR3t P4s5\(rs/\(rs/0rd\(dq .B vmm useradd e.user@example.com Enter new password: Retype new password: .fi .\" ------------------------------------ .SS userdelete (ud) .BI "vmm userdelete" " address" .RB [ force ] .PP Use this subcommand to delete the account with the given .IR address . .PP If there are one or more aliases with an identical destination address, .B vmm will abort the requested operation and show an error message. To prevent this, specify the optional keyword .BR force . .\" ------------------------------------ .SS userinfo (ui) .B "vmm userinfo" .IR address " [" details ] .PP This subcommand displays some information about the account specified by .IR address . .PP If the optional argument .I details is given some more information will be displayed. Possible values for .I details are: .RS .TP 8 .B aliases to list all alias addresses with the destination .I address .TP .B du to display the disk usage of the user's mail directory. In order to summarize the disk usage each time this subcommand is executed automatically, set .I account.disk_usage in your .I vmm.cfg to .BR true . .TP .B full to list all information mentioned above .RE .PP Example: .PP .nf .B vmm ui d.user@example.com Account information ------------------- Address..........: d.user@example.com Name.............: None UID..............: 79881 GID..............: 70704 Home.............: /srv/mail/2/70704/79881 Mail_Location....: mdbox:~/mdbox Quota Storage....: [ 0.00%] 0/500.00 MiB Quota Messages...: [ 0.00%] 0/10,000 Transport........: lmtp:unix:private/dovecot-lmtp SMTP.............: disabled POP3.............: disabled IMAP.............: enabled SIEVE............: enabled .fi .\" ------------------------------------ .SS username (un) .BI "vmm username" " address" .RI [ name ] .PP The user's real .I name can be set/updated with this subcommand. .PP If no .I name is given, the value stored for the account is erased. .PP Example: .PP .nf .B vmm un d.user@example.com \(dqJohn Doe\(dq .fi .\" ------------------------------------ .SS userpassword (up) .BI "vmm userpassword" " address" .RI [ password ] .PP The password of an account can be updated with this subcommand. .PP If no .I password was provided, .B vmm will prompt for it interactively. .PP Example: .PP .nf .B vmm up d.user@example.com \(dqA |\(rs/|0r3 5ecur3 P4s5\(rs/\(rs/0rd?\(dq .fi .\" ------------------------------------ .SS usernote (uo) .BI "vmm usernote" " address" .RI [ note ] .PP With this subcommand, it is possible to attach a note to the specified account. Without an argument, an existing note is removed. .PP Example: .PP .nf .B vmm uo d.user@example.com Only needed until end of May 2012 .fi .\" ------------------------------------ .SS userquota (uq) .BI "vmm userquota" " address storage" .RI [ messages ] .PP This subcommand is used to set a new quota limit for the given account. .PP When the argument .I messages was omitted the default number of messages .B 0 (zero) will be applied. .PP Instead of .I storage pass the keyword .B domain to remove the account\-specific override, causing the domain's value to be in effect. .PP Example: .PP .nf .B vmm userquota d.user@example.com 750m .fi .\" ------------------------------------ .SS userservices (us) .B vmm userservices .IR address " [" "service ..." ] .PP To grant a user access to the specified services, use this command. .PP All omitted services will be deactivated/unusable for the user with the given .IR address . .PP Instead of .I service pass 'domain' to remove the account\-specific override, causing the domain's value to be in effect. .PP Example: .PP .nf .B vmm userservices d.user@example.com SMTP IMAP .\" ------------------------------------ .SS usertransport (ut) .BI "vmm usertransport" " address transport" .PP A different .I transport for an account can be specified with this subcommand. .PP Instead of .I transport pass 'domain' to remove the account\-specific override, causing the domain's value to be in effect. .PP Example: .br Assumed you want to use Dovecot's .BR dsync (1) to convert a user's mailbox from Maildir format to mdbox format, you can tell Postfix to retry later. .PP .nf .B vmm ut d.user@example.com \(dqretry:4.0.0 Mailbox being migrated\(dq # convert the mailbox ... then set the transport to Dovecot's lmtp .B vmm ut d.user@example.com lmtp:unix:private/dovecot\-lmtp .fi .\" ----------------------------------------------------------------------- .SH ALIAS SUBCOMMANDS .SS aliasadd (aa) .BI "vmm aliasadd" " address destination ..." .PP This subcommand is used to create a new alias .I address with one or more .I destination addresses. .PP Within the destination address, the placeholders .IR %n , .IR %d , and .IR %= will be replaced by the local part, the domain, or the email address with '@' replaced by '=' respectively. In combination with alias domains, this enables domain\-specific destinations. .PP Examples: .PP .nf .B vmm aliasadd john.doe@example.com d.user@example.com .B vmm aa support@example.com d.user@example.com e.user@example.com .B vmm aa postmaster@example.com postmaster+%d@example.org .fi .\" ------------------------------------ .SS aliasdelete (ad) .BI "vmm aliasdelete" " address" .RI [ destination " ...]" .PP This subcommand is used to delete one or multiple .IR destination s from the alias with the given .IR address . .PP When no .I destination address was specified the alias with all its destinations will be deleted. .PP Example: .PP .nf .B vmm ad support@example.com d.user@example.com .fi .\" ------------------------------------ .SS aliasinfo (ai) .BI "vmm aliasinfo" " address" .PP Information about the alias with the given .I address can be displayed with this subcommand. .PP Example: .PP .nf .B vmm aliasinfo support@example.com Alias information ----------------- Mail for support@example.com will be redirected to: * e.user@example.com .fi .\" ----------------------------------------------------------------------- .SH RELOCATED SUBCOMMANDS .SS relocatedadd (ra) .BI "vmm relocatedadd" " address newaddress" .PP A new relocated user can be created with this subcommand. .PP .I address is the user's ex\-email address, for example b.user@example.com, and .I newaddress points to the new email address where the user can be reached. .PP Example: .PP .nf .B vmm relocatedadd b.user@example.com b\-user@company.tld .fi .\" ------------------------------------ .SS relocatedinfo (ri) .BI "vmm relocatedinfo " address .PP This subcommand shows the new address of the relocated user with the given .IR address . .PP Example: .PP .nf .B vmm relocatedinfo b.user@example.com Relocated information --------------------- User `b.user@example.com' has moved to `b\-user@company.tld' .fi .\" ------------------------------------ .SS relocateddelete (rd) .BI "vmm relocateddelete " address .PP Use this subcommand in order to delete the relocated user with the given .IR address . .PP Example: .PP .nf .B vmm relocateddelete b.user@example.com .fi .\" ----------------------------------------------------------------------- .SH CATCH\-ALL SUBCOMMANDS .SS catchalladd (caa) .BI "vmm catchalladd" " fqdn destination ..." .PP This subcommand allows to specify destination addresses for a domain, which shall receive mail addressed to unknown local parts within that domain. Those catch\-all aliases hence \(dqcatch all\(dq mail to any address in the domain (unless a more specific alias, mailbox or relocated entry exists). .PP WARNING: Catch\-all addresses can cause mail server flooding because spammers like to deliver mail to all possible combinations of names, e.g. to all addresses between abba@example.org and zztop@example.org. .PP Example: .PP .nf .B vmm catchalladd example.com user@example.org .fi .\" ------------------------------------ .SS catchallinfo (cai) .BI "vmm catchallinfo " fqdn .PP This subcommand displays information about catch\-all aliases defined for a domain. .PP Example: .PP .nf .B vmm catchallinfo example.com Catch-all information --------------------- Mail to unknown localparts in domain example.com will be sent to: * user@example.org .fi .\" ------------------------------------ .SS catchalldelete (cad) .BI "vmm catchalldelete " fqdn .RI [ destination " ...]" .PP With this subcommand, catch\-all aliases defined for a domain can be removed, either all of them, or those .IR destination s which were specified explicitly. .PP Example: .PP .nf .B vmm catchalldelete example.com user@example.com .fi .\" ----------------------------------------------------------------------- .SH FILES .TP .I /root/vmm.cfg will be used when found. .TP .I /usr/local/etc/vmm.cfg will be used when the above file doesn't exist. .TP .I /etc/vmm.cfg will be used when none of the both above mentioned files exists. .\" ----------------------------------------------------------------------- .SH SEE ALSO .BR dsync (1), .BR transport (5), .BR vmm.cfg (5) .\" ----------------------------------------------------------------------- .SH INTERNET RESOURCES .TP Homepage http://vmm.localdomain.org/ .TP Project site http://sf.net/projects/vmm/ .TP Bug tracker https://bitbucket.org/pvo/vmm/issues .\" ----------------------------------------------------------------------- .SH COPYING vmm and its manual pages were written by Pascal Volk and are licensed under the terms of the BSD License.vmm-0.6.1/man/man5/0000755000175000017500000000000012033032424012113 5ustar pvopvovmm-0.6.1/man/man5/vmm.cfg.50000644000175000017500000003642312033032424013546 0ustar pvopvo.TH "VMM.CFG" "5" "2012-08-12" "vmm 0.6" "vmm" .SH NAME vmm.cfg \- configuration file for vmm .\" ----------------------------------------------------------------------- .SH SYNOPSIS vmm.cfg .\" ----------------------------------------------------------------------- .SH DESCRIPTION .BR vmm (1) reads its configuration data from .IR vmm.cfg . .PP The configuration file is split into multiple sections. A section starts with the section name, enclosed in square brackets .RB ` [ "' and `" ] ', followed by .RI ` option " = " value ' pairs. .br Whitespace around the `=' and at the end of a value is ignored. Empty lines and lines starting with `#' or `;' will be ignored. .PP Each value uses one of the following data types: .TP 8 .I Boolean to indicate if something is enabled/activated (true) or disabled/deactivated (false). .br Accepted values for .I true are: .BR 1 , " yes" , " true" " and " on . .br Accepted values for .I false are: .BR 0 , " no" , " false" " and " off . .TP .I Int an integer number, written without a fractional or decimal component. .br For example .BR 1 , " 50" " or " 321 are integers. .TP .I String a sequence of characters and/or numbers. .br For example .RB ` word "', `" "hello world" "' or `" /usr/bin/strings ' are strings. .PP Most options have a default value, shown in parentheses after the option's name. In order to use a option's default setting, comment out the line, either with a .BR # " or " ; or simply remove the setting from .IR vmm.cfg . .PP A minimal .I vmm.cfg would be: .PP .nf [database] user = me pass = xxxxxxxx [misc] dovecot_version = 1.2.16 .fi .\" ----------------------------------------------------------------------- .SH SEARCH ORDER By default .BR vmm (1) looks for the .I vmm.cfg file in the following directories in the order listed: .RS .PD 0 .TP .I /root .TP .I /usr/local/etc .TP .I /etc .PD .RE .PP The first configuration file found will be used. .\" ----------------------------------------------------------------------- .SH SECTION ACCOUNT The options in the section .B account are used to specify user account related settings. .SS account.delete_directory .BR delete_directory " (default: false) :" .I Boolean .PP Determines the behavior of .BR vmm (1) when an account is deleted (userdelete). If this option is set to .I true the user's home directory will be deleted recursively. .\" ------------------------------------ .SS account.directory_mode .BR directory_mode " (default: 448) :" .I Int .PP Access mode for a user's home directory and all directories inside. The value has to be specified in decimal (base 10) notation. .br For example: `drwx\-\-\-\-\-\-' \(-> octal 0700 \(-> decimal 448 .\" ------------------------------------ .SS account.disk_usage .BR disk_usage " (default: false) :" .I Boolean .PP Determines whether the disk usage of a user's mail directory always should be summarized, using .BR du (1), and displayed with the account information (userinfo). .PP This could be slow on large Maildirs. When you have enabled quotas, .BR vmm 's userinfo subcommand will also display the current quota usage of the account. You may also use userinfo's optional details\-argument .BR du " or " full , in order to display the current disk usage of an account's mail directory. .\" ------------------------------------ .SS account.password_length .BR password_length " (default: 8) :" .I Int .PP Determines how many characters and/or numbers should be used for randomly generated passwords. Any value less than 8 will be increased to 8. .\" ------------------------------------ .SS account.random_password .BR random_password " (default: false) :" .I Boolean .PP Determines whether .BR vmm (1) should generate a random password when no password was given for the useradd subcommand. If this option is set to .I false .B vmm will prompt you to enter a password for the new account. .PP You can specify the password length of generated passwords with the .I account.password_length option. .\" ----------------------------------------------------------------------- .SH SECTION BIN The .B bin section is used to specify some paths to some binaries required by .BR vmm (1). .SS bin.dovecotpw .BR dovecotpw " (default: /usr/sbin/dovecotpw) :" .I String .PP The absolute path to the .BR dovecotpw (1) binary. Use the absolute path to the .BR doveadm (1) binary, if you are using Dovecot v2.0. .PP This binary is used to generate a password hash, if .I misc.password_scheme is set to one of `CRAM\-MD5', `HMAC\-MD5', `LANMAN', `OTP', `RPA' or `SKEY'. This binary will be also required if your Python installation doesn't support the: .IP \(bu 4 md4 hash algorithm (hashlib + OpenSSL or PyCrypto) used for the password schemes: `PLAIN\-MD4' and `NTLM' .IP \(bu sha256 hash algorithm (hashlib or PyCrypto \(>= 2.1.0alpha1) used for the password schemes: `SHA256' and `SSHA256' .IP \(bu sha512 hash algorithm (hashlib) used for the password schemes: `SHA512' and `SSHA512' .PP The .BR doveadm (1) binary is also used to create a user's INBOX and additional mailboxes .RI ( mailbox.folders ), when the .I mailbox.format is set to .BR mdbox " or " sdbox . .\" ------------------------------------ .SS bin.du .BR du " (default: /usr/bin/du) :" .I String .PP The absolute path to .BR du (1). This binary is used to summarize the disk usage of a user's mail directory. .\" ------------------------------------ .SS bin.postconf .BR postconf " (default: /usr/sbin/postconf) :" .I String .PP The absolute path to Postfix' .BR postconf (1). This binary is required when .BR vmm (1) has to check for some Postfix settings, e.g. the .IR virtual_alias_expansion_limit . .\" ----------------------------------------------------------------------- .SH SECTION DATABASE The .B database section is used to specify some options required to connect to the database. .SS database.host .BR host " (default: localhost) :" .I String .PP Hostname or IP address of the database server. .\" ------------------------------------ .SS database.module .BR module " (default: psycopg2) :" .I String .PP The Python PostgreSQL database adapter module to be used. Supported modules are .BR psycopg2 " and " pyPgSQL . .\" ------------------------------------ .SS database.name .BR name " (default: mailsys) :" .I String .PP Name of the database. .\" ------------------------------------ .SS database.pass .BR pass " (default: " None ") :" .I String .PP Database password. .\" ------------------------------------ .SS database.port .BR port " (default: 5432) :" .I Int .PP The TCP port, on which the database server is listening for connections. .\" ------------------------------------ .SS database.sslmode .BR sslmode " (default: prefer) :" .I String .PP Determines whether and with what priority an SSL connection will be negotiated with the database server. Possible values are: .BR disabled ", " allow ", " prefer ", " require ", " verify\-ca " and " .BR verify\-full . The modes .BR verify\-ca " and " verify\-full are available since PostgreSQL 8.4 .PP This setting will be ignored when the .I database.module is set to .BR pyPgSQL . .\" ------------------------------------ .SS database.user .BR user " (default: " None ") :" .I String .PP Name of the database user. .\" ----------------------------------------------------------------------- .SH SECTION DOMAIN The .B domain section specifies some domain related settings. .PP The quota limit (quota_bytes and quota_messages), service settings (imap, pop3, sieve and smtp) and the transport setting will be applied when a domain is created. In order to modify those settings for an existing domain, use one of the following .BR vmm (1) subcommands: .PP .TP .B domainquota in order to update a domain's quota limit .TP .B domainservices in order to assign a different service set to a domain .TP .B domaintransport in order to set a new default domain transport .PP When an account is created, it inherits all the settings of the domain to which it is added. Different settings for an existing account can be set using the subcommands .BR userquota ", " userservices " and " usertransport . .\" ------------------------------------ .SS domain.auto_postmaster .BR auto_postmaster " (default: true) :" .I Boolean .PP Determines if .BR vmm (1) should create also a postmaster account when a new domain is created (domainadd). .\" ------------------------------------ .SS domain.delete_directory .BR delete_directory " (default: false) :" .I Boolean .PP Specifies whether the domain directory and all user directories inside should be deleted when a domain is deleted (domaindelete). .\" ------------------------------------ .SS domain.directory_mode .BR directory_mode " (default: 504) :" .I Int .PP Access mode for the domain directory in decimal (base 10) notation. .br For example: `drwxrwx\-\-\-' \(-> octal 0770 \(-> decimal 504 .\" ------------------------------------ .SS domain.force_deletion .BR force_deletion " (default: false) :" .I Boolean .PP Force the deletion of accounts and aliases when a domain is deleted (domaindelete). .\" ------------------------------------ .SS domain.imap .BR imap " (default: true) :" .I Boolean .PP Determines whether newly created users can log in via IMAP. .\" ------------------------------------ .SS domain.pop3 .BR pop3 " (default: true) :" .I Boolean .PP Determines whether newly created users can log in via POP3. .\" ------------------------------------ .SS domain.quota_bytes .BR quota_bytes " (default: 0) :" .I String .PP Quota limit in bytes. 0 means unlimited. This limit will be applied to all newly created domains. .PP The option's value can be written as an integer value, e.g.: .BR 20480 . It's also possible to append one of the following prefixes to the limit: .BR b " (bytes), " k " (kilobytes), " M " (megabytes) or " G (gigabytes). .br 1024 is the same as 1024b or 1k. .\" ------------------------------------ .SS domain.quota_messages .BR quota_messages " (default: 0) :" .I Int .PP Quota limit in number of messages. 0 means unlimited. This limit will be applied to all newly created domains. .\" ------------------------------------ .SS domain.sieve .BR sieve " (default: true) :" .I Boolean .PP Determines whether newly created users can log in via SIEVE (ManageSieve). .\" ------------------------------------ .SS domain.smtp .BR smtp " (default: true) :" .I Boolean .PP Determines whether newly created users can log in via SMTP (SMTP AUTH). .\" ------------------------------------ .SS domain.transport .BR transport " (default: dovecot:) :" .I String .PP Default transport for domains and accounts. For details see .BR transport (5). .\" ----------------------------------------------------------------------- .SH SECTION MAILBOX The .B mailbox section is used to specify some options for new created mailboxes in the users home directories. The INBOX will be created always. .SS mailbox.folders .BR folders " (default: Drafts:Sent:Templates:Trash) :" .I String .PP A colon separated list of mailboxes that should be created. If no additionally mailboxes should be created, set the value of this option to a single colon .RB (` : '). .PP If you want to create folders containing one or more subfolders, separate them with a single dot .RB (` . '). .PP If you want to use internationalized mailbox names (e.g. `Wysłane' or `Gelöschte Objekte'), write their names UTF\-8 encoded. .BR vmm (1) will convert internationalized mailbox names to a modified version of the UTF\-7 encoding (see also: RFC 3501, section 5.1.3). .\" ------------------------------------ .SS mailbox.format .BR format " (default: maildir) :" .I String .PP The mailbox format to be used for a user's mailbox. Depending on the used Dovecot version .RI ( misc.dovecot_version ) .BR vmm (1) supports up to three formats: .TP 8 .B maildir Dovecot \(>= v1.0.0 .TP .B mdbox Dovecot \(>= v2.0.beta5 .TP .B sdbox Dovecot \(>= v2.0.rc3 .\" ------------------------------------ .SS mailbox.root .BR root " (default: Maildir) :" .I String .PP Name of the mailbox root directory in a user's home directory. Commonly used names, depending on the used .IR mailbox.format , are .BR Maildir ", " mdbox " or " sdbox . .\" ------------------------------------ .SS mailbox.subscribe .BR subscribe " (default: true) :" .I Boolean .PP When this option is set to .BR true , the mailboxes from the .I mailbox.folders option will be listed in the user's subscriptions file. If you don't want to subscribe the created mailboxes, set this option to .BR false . .\" ----------------------------------------------------------------------- .SH SECTION MISC The .I misc section is used to define miscellaneous settings. .SS misc.base_directory .BR base_directory " (default: /srv/mail) :" .I String .PP All domain directories will be created inside this directory. .\" ------------------------------------ .SS misc.crypt_blowfish_rounds .BR crypt_blowfish_rounds " (default: 5) :" .I Int .PP Number of encryption rounds for the .I password_scheme .BR BLF\-CRYPT . .PP The value must be in range .BR 4 " \- " 31 . .\" ------------------------------------ .SS misc.crypt_sha256_rounds .BR crypt_sha256_rounds " (default: 5000) :" .I Int .PP Number of encryption rounds for the .I password_scheme .BR SHA256\-CRYPT . .PP The value must be in range .BR 1000 " \- " 999999999 . .\" ------------------------------------ .SS misc.crypt_sha512_rounds .BR crypt_sha512_rounds " (default: 5000) :" .I Int .PP Number of encryption rounds for the .I password_scheme .BR SHA512\-CRYPT . .PP The value must be in range .BR 1000 " \- " 999999999 . .\" ------------------------------------ .SS misc.dovecot_version .BR dovecot_version " (default: " None ") :" .I String .PP The version number of the currently used Dovecot version. (see: .BR "dovecot \-\-version" ) .br When, for example, the command .B dovecot \-\-version prints .IR "2.0.beta4 (8818db00d347)" , set the value of this option to .BR 2.0.beta4 . .\" ------------------------------------ .SS misc.password_scheme .BR password_scheme " (default: CRAM\-MD5) :" .I String .PP Password scheme to use. To get a list of all usable password schemes execute the command .BR "vmm lp" . .PP With Dovecot \(>= v1.1.alpha1 it is also possible to append an encoding suffix to the password_scheme. Supported encoding suffixes are: .BR .b64 ", " .base64 " and " .hex . For example: PLAIN.BASE64 .\" ----------------------------------------------------------------------- .SH EXAMPLE An example configuration. All options that are not listed in the configuration file will have their default values. .PP .nf [account] password_length = 10 random_password = true [bin] dovecotpw = /usr/bin/doveadm [database] host = dbsrv8.example.net pass = PY_SRJ}L/0p\-oOk port = 5433 sslmode = require user = vmm [domain] quota_bytes = 500M quota_messages = 10000 transport = lmtp:unix:private/dovecot\-lmtp [mailbox] folders = Drafts:Sent:Templates:Trash:Lists.Dovecot:Lists.Postfix [misc] crypt_sha512_rounds = 10000 dovecot_version = 2.0.beta4 password_scheme = SHA512\-CRYPT.hex .fi .\" ----------------------------------------------------------------------- .SH SEE ALSO .BR postconf (1), .BR vmm (1), .BR transport (5) .\" ----------------------------------------------------------------------- .SH INTERNET RESOURCES .TP Homepage http://vmm.localdomain.org/ .TP Project site http://sf.net/projects/vmm/ .TP Bug tracker https://bitbucket.org/pvo/vmm/issues .\" ----------------------------------------------------------------------- .SH COPYING vmm and its manual pages were written by Pascal Volk and are licensed under the terms of the BSD License.vmm-0.6.1/Configure.Dovecot_20000644000175000017500000001127312033032424014173 0ustar pvopvo# This document contains a minimal configuration for a vmm setup with # Dovecot v2.x. # # You could save this file as local.conf in the dovecot configuration directory # (commonly /etc/dovecot or /usr/local/etc/dovecot). # When you want to use this file as your configuration file for Dovecot, make # sure you have commented out the line "!include conf.d/*.conf". The last line # "!include_try local.conf" is sufficient. # # Otherwise you have to apply the following settings to the configuration files # in the conf.d directory. ### # dovecot.conf ### protocols = imap lmtp # uncomment if your users should be able to manage their sieve scripts #protocols = imap lmtp sieve # uncomment if you want to use the quota plugin #dict { # quota = pgsql:/usr/local/etc/dovecot/dovecot-dict-sql.conf.ext #} ### # conf.d/10-auth.conf ### auth_mechanisms = plain login cram-md5 passdb { driver = sql args = /usr/local/etc/dovecot/dovecot-sql.conf.ext } userdb { driver = sql args = /usr/local/etc/dovecot/dovecot-sql.conf.ext } #!include auth-system.conf.ext ### # conf.d/10-mail.conf ### first_valid_gid = 70000 first_valid_uid = 70000 mail_access_groups = dovemail mail_location = maildir:~/Maildir # uncomment if you want to use the quota plugin #mail_plugins = quota ### # conf.d/10-master.conf ### # if you don't want to use secure imap, you have to disable the imaps listener ##service imap-login { ## inet_listener imaps { ## port = 0 ## } ##} service lmtp { unix_listener /var/spool/postfix/private/dovecot-lmtp { user = postfix group = postfix mode = 0600 } } service auth { user = doveauth unix_listener auth-userdb { } unix_listener /var/spool/postfix/private/dovecot-auth { user = postfix group = postfix mode = 0600 } } service auth-worker { unix_listener auth-worker { user = doveauth group = $default_internal_user mode = 0660 } user = doveauth } service dict { unix_listener dict { group = dovemail mode = 0660 } } ### # conf.d/10-ssl.conf ### # SSL/TLS support: yes, no, required. #ssl = yes ssl_cert = 0: raise ADErr(_(u"The alias domain '%s' already exists.") % self._name, ALIASDOMAIN_EXISTS) if not self._domain: raise ADErr(_(u'No destination domain set for the alias domain.'), ALIASDOMAIN_NO_DOMDEST) if self._domain.gid < 1: raise ADErr(_(u"The target domain '%s' does not exist.") % self._domain.name, NO_SUCH_DOMAIN) dbc = self._dbh.cursor() dbc.execute('INSERT INTO domain_name (domainname, gid, is_primary) ' 'VALUES (%s, %s, FALSE)', (self._name, self._domain.gid)) self._dbh.commit() dbc.close() self._gid = self._domain.gid def info(self): """Returns a dict (keys: "alias" and "domain") with the names of the AliasDomain and its primary domain.""" if self._gid < 1: raise ADErr(_(u"The alias domain '%s' does not exist.") % self._name, NO_SUCH_ALIASDOMAIN) dbc = self._dbh.cursor() dbc.execute('SELECT domainname FROM domain_name WHERE gid = %s AND ' 'is_primary', (self._gid,)) domain = dbc.fetchone() dbc.close() if domain: return {'alias': self._name, 'domain': domain[0]} else: # an almost unlikely case, isn't it? raise ADErr(_(u'There is no primary domain for the alias domain ' u"'%s'.") % self._name, NO_SUCH_DOMAIN) def switch(self): """Switch the destination of the AliasDomain to the new destination, set with the method `set_destination()`. """ if not self._domain: raise ADErr(_(u'No destination domain set for the alias domain.'), ALIASDOMAIN_NO_DOMDEST) if self._domain.gid < 1: raise ADErr(_(u"The target domain '%s' does not exist.") % self._domain.name, NO_SUCH_DOMAIN) if self._gid < 1: raise ADErr(_(u"The alias domain '%s' does not exist.") % self._name, NO_SUCH_ALIASDOMAIN) if self._gid == self._domain.gid: raise ADErr(_(u"The alias domain '%(alias)s' is already assigned " u"to the domain '%(domain)s'.") % {'alias': self._name, 'domain': self._domain.name}, ALIASDOMAIN_EXISTS) dbc = self._dbh.cursor() dbc.execute('UPDATE domain_name SET gid = %s WHERE gid = %s AND ' 'domainname = %s AND NOT is_primary', (self._domain.gid, self._gid, self._name)) self._dbh.commit() dbc.close() self._gid = self._domain.gid def delete(self): """Delete the AliasDomain's record form the database. Raises an AliasDomainError if the AliasDomain doesn't exist. """ if self._gid < 1: raise ADErr(_(u"The alias domain '%s' does not exist.") % self._name, NO_SUCH_ALIASDOMAIN) dbc = self._dbh.cursor() dbc.execute('DELETE FROM domain_name WHERE domainname = %s AND NOT ' 'is_primary', (self._name,)) if dbc.rowcount > 0: self._dbh.commit() self._gid = 0 dbc.close() del _ vmm-0.6.1/VirtualMailManager/quotalimit.py0000644000175000017500000000734312033032424016775 0ustar pvopvo# -*- coding: UTF-8 -*- # Copyright (c) 2011 - 2012, Pascal Volk # See COPYING for distribution information. """ VirtualMailManager.quotalimit ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Virtual Mail Manager's QuotaLimit class to manage quota limits for domains and accounts. """ from VirtualMailManager.pycompat import all _ = lambda msg: msg class QuotaLimit(object): """Class to handle quota limit specific data.""" __slots__ = ('_dbh', '_qid', '_bytes', '_messages') _kwargs = ('qid', 'bytes', 'messages') def __init__(self, dbh, **kwargs): """Create a new QuotaLimit instance. Either the `qid` keyword or the `bytes` and `messages` keywords must be specified. Arguments: `dbh` : pyPgSQL.PgSQL.Connection || psycopg2._psycopg.connection A database connection for the database access. Keyword arguments: `qid` : int The id of a quota limit `bytes` : long The quota limit in bytes. `messages` : int The quota limit in number of messages """ self._dbh = dbh self._qid = 0 self._bytes = 0 self._messages = 0 for key in kwargs.iterkeys(): if key not in self.__class__._kwargs: raise ValueError('unrecognized keyword: %r' % key) qid = kwargs.get('qid') if qid is not None: assert isinstance(qid, (int, long)) self._load_by_qid(qid) else: bytes_, msgs = kwargs.get('bytes'), kwargs.get('messages') assert all(isinstance(i, (int, long)) for i in (bytes_, msgs)) if bytes_ < 0: self._bytes = -bytes_ else: self._bytes = bytes_ if msgs < 0: self._messages = -msgs else: self._messages = msgs self._load_by_limit() @property def bytes(self): """Quota limit in bytes.""" return self._bytes @property def messages(self): """Quota limit in number of messages.""" return self._messages @property def qid(self): """The quota limit's unique ID.""" return self._qid def __eq__(self, other): if isinstance(other, self.__class__): return self._qid == other._qid return NotImplemented def __ne__(self, other): if isinstance(other, self.__class__): return self._qid != other._qid return NotImplemented def _load_by_limit(self): """Load the quota limit by limit values from the database.""" dbc = self._dbh.cursor() dbc.execute('SELECT qid FROM quotalimit WHERE bytes = %s AND ' 'messages = %s', (self._bytes, self._messages)) res = dbc.fetchone() dbc.close() if res: self._qid = res[0] else: self._save() def _load_by_qid(self, qid): """Load the quota limit by its unique ID from the database.""" dbc = self._dbh.cursor() dbc.execute('SELECT bytes, messages FROM quotalimit WHERE qid = %s', (qid,)) res = dbc.fetchone() dbc.close() if not res: raise ValueError('Unknown quota limit id specified: %r' % qid) self._qid = qid self._bytes, self._messages = res def _save(self): """Store a new quota limit in the database.""" dbc = self._dbh.cursor() dbc.execute("SELECT nextval('quotalimit_id')") self._qid = dbc.fetchone()[0] dbc.execute('INSERT INTO quotalimit (qid, bytes, messages) VALUES ' '(%s, %s, %s)', (self._qid, self._bytes, self._messages)) self._dbh.commit() dbc.close() del _ vmm-0.6.1/VirtualMailManager/errors.py0000644000175000017500000000267412033032424016123 0ustar pvopvo# -*- coding: UTF-8 -*- # Copyright (c) 2007 - 2012, Pascal Volk # See COPYING for distribution information. """ VirtualMailManager.errors ~~~~~~~~~~~~~~~~~~~~~~~~~ VMM's Exception classes """ class VMMError(Exception): """Exception base class for VirtualMailManager exceptions""" def __init__(self, msg, code): Exception.__init__(self, msg) self.msg = msg self.code = int(code) def __repr__(self): return '%s(%r, %r)' % (self.__class__.__name__, self.msg, self.code) class ConfigError(VMMError): """Exception class for configuration exceptions""" pass class PermissionError(VMMError): """Exception class for permissions exceptions""" pass class NotRootError(VMMError): """Exception class for non-root exceptions""" pass class DomainError(VMMError): """Exception class for Domain exceptions""" pass class AliasDomainError(VMMError): """Exception class for AliasDomain exceptions""" pass class AccountError(VMMError): """Exception class for Account exceptions""" pass class AliasError(VMMError): """Exception class for Alias exceptions""" pass class EmailAddressError(VMMError): """Exception class for EmailAddress exceptions""" pass class MailLocationError(VMMError): """Exception class for MailLocation exceptions""" pass class RelocatedError(VMMError): """Exception class for Relocated exceptions""" pass vmm-0.6.1/VirtualMailManager/constants.py0000644000175000017500000000344212033032424016615 0ustar pvopvo# -*- coding: UTF-8 -*- # Copyright (c) 2007 - 2012, Pascal Volk # See COPYING for distribution information. """ VirtualMailManager.constants ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ VirtualMailManager's constants: * version information * upper and lower limits MIN_* / MAX_* * exit codes * error codes """ # version information __all__ = ['__author__', '__date__', '__version__'] AUTHOR = 'Pascal Volk ' RELDATE = '2012-10-03' VERSION = '0.6.1' __author__ = AUTHOR __copyright__ = 'Copyright (c) 2007-2012 %s' % __author__ __date__ = RELDATE __version__ = VERSION # limits MIN_GID = 70000 MIN_UID = 70000 # exit codes EX_SUCCESS = 0 EX_MISSING_ARGS = 1 EX_UNKNOWN_COMMAND = 2 EX_USER_INTERRUPT = 3 # error codes ACCOUNT_AND_ALIAS_PRESENT = 20 ACCOUNT_EXISTS = 21 ACCOUNT_MISSING_PASSWORD = 69 ALIASDOMAIN_EXISTS = 23 ALIASDOMAIN_ISDOMAIN = 24 ALIASDOMAIN_NO_DOMDEST = 25 ALIAS_EXCEEDS_EXPANSION_LIMIT = 27 ALIAS_EXISTS = 28 ALIAS_PRESENT = 30 CONF_ERROR = 31 CONF_NOFILE = 32 CONF_NOPERM = 33 CONF_WRONGPERM = 34 DATABASE_ERROR = 35 DOMAINDIR_GROUP_MISMATCH = 36 DOMAIN_ALIAS_EXISTS = 37 DOMAIN_EXISTS = 38 DOMAIN_INVALID = 39 DOMAIN_NO_NAME = 40 DOMAIN_TOO_LONG = 41 FOUND_DOTS_IN_PATH = 42 INVALID_ADDRESS = 43 INVALID_ARGUMENT = 44 INVALID_MAIL_LOCATION = 70 INVALID_SECTION = 46 LOCALPART_INVALID = 47 LOCALPART_TOO_LONG = 48 MAILDIR_PERM_MISMATCH = 49 MAILLOCATION_INIT = 50 NOT_EXECUTABLE = 51 NO_SUCH_ACCOUNT = 52 NO_SUCH_ALIAS = 53 NO_SUCH_ALIASDOMAIN = 54 NO_SUCH_BINARY = 55 NO_SUCH_DIRECTORY = 56 NO_SUCH_DOMAIN = 57 NO_SUCH_RELOCATED = 58 RELOCATED_ADDR_DEST_IDENTICAL = 59 RELOCATED_EXISTS = 60 UNKNOWN_SERVICE = 65 VMM_ERROR = 67 VMM_TOO_MANY_FAILURES = 68 # address types TYPE_ACCOUNT = 0x1 TYPE_ALIAS = 0x2 TYPE_RELOCATED = 0x4 vmm-0.6.1/VirtualMailManager/account.py0000644000175000017500000004362212033032424016241 0ustar pvopvo# -*- coding: UTF-8 -*- # Copyright (c) 2007 - 2012, Pascal Volk # See COPYING for distribution information. """ VirtualMailManager.account ~~~~~~~~~~~~~~~~~~~~~~~~~~ Virtual Mail Manager's Account class to manage e-mail accounts. """ from VirtualMailManager.common import version_str, \ format_domain_default from VirtualMailManager.constants import \ ACCOUNT_EXISTS, ACCOUNT_MISSING_PASSWORD, ALIAS_PRESENT, \ INVALID_ARGUMENT, INVALID_MAIL_LOCATION, NO_SUCH_ACCOUNT, \ NO_SUCH_DOMAIN, VMM_ERROR from VirtualMailManager.common import validate_transport from VirtualMailManager.domain import Domain from VirtualMailManager.emailaddress import EmailAddress from VirtualMailManager.errors import VMMError, AccountError as AErr from VirtualMailManager.maillocation import MailLocation from VirtualMailManager.password import pwhash from VirtualMailManager.quotalimit import QuotaLimit from VirtualMailManager.transport import Transport from VirtualMailManager.serviceset import ServiceSet __all__ = ('Account', 'get_account_by_uid') _ = lambda msg: msg cfg_dget = lambda option: None class Account(object): """Class to manage e-mail accounts.""" __slots__ = ('_addr', '_dbh', '_domain', '_mail', '_new', '_passwd', '_qlimit', '_services', '_transport', '_note', '_uid') def __init__(self, dbh, address): """Creates a new Account instance. When an account with the given *address* could be found in the database all relevant data will be loaded. Arguments: `dbh` : pyPgSQL.PgSQL.Connection A database connection for the database access. `address` : VirtualMailManager.EmailAddress.EmailAddress The e-mail address of the (new) Account. """ if not isinstance(address, EmailAddress): raise TypeError("Argument 'address' is not an EmailAddress") self._addr = address self._dbh = dbh self._domain = Domain(self._dbh, self._addr.domainname) if not self._domain.gid: # TP: Hm, what “quotation marks” should be used? # If you are unsure have a look at: # http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage raise AErr(_(u"The domain '%s' does not exist.") % self._addr.domainname, NO_SUCH_DOMAIN) self._uid = 0 self._mail = None self._qlimit = None self._services = None self._transport = None self._note = None self._passwd = None self._new = True self._load() def __nonzero__(self): """Returns `True` if the Account is known, `False` if it's new.""" return not self._new def _load(self): """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, 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, _note = result def load_helper(ctor, own, field, dbresult): # Py25: cur = None if own is None else getattr(own, field) if own is None: cur = None else: cur = getattr(own, field) if cur != dbresult: kwargs = {field: dbresult} if dbresult is None: return dbresult else: return ctor(self._dbh, **kwargs) self._qlimit = load_helper(QuotaLimit, self._qlimit, 'qid', _qid) self._services = load_helper(ServiceSet, self._services, 'ssid', _ssid) 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): """Set the unique ID for the new Account.""" assert self._uid == 0 dbc = self._dbh.cursor() dbc.execute("SELECT nextval('users_uid')") self._uid = dbc.fetchone()[0] dbc.close() def _prepare(self, maillocation): """Check and set different attributes - before we store the information in the database. """ if maillocation.dovecot_version > cfg_dget('misc.dovecot_version'): raise AErr(_(u"The mailbox format '%(mbfmt)s' requires Dovecot " u">= v%(version)s.") % { 'mbfmt': maillocation.mbformat, 'version': version_str(maillocation.dovecot_version)}, INVALID_MAIL_LOCATION) transport = self._transport or self._domain.transport validate_transport(transport, maillocation) self._mail = maillocation self._set_uid() def _update_tables(self, column, value): """Update various columns in the users table. Arguments: `column` : basestring Name of the table column. Currently: qid, ssid and tid `value` : long The referenced key """ if column not in ('qid', 'ssid', 'tid'): raise ValueError('Unknown column: %r' % column) dbc = self._dbh.cursor() dbc.execute('UPDATE users SET %s = %%s WHERE uid = %%s' % column, (value, self._uid)) if dbc.rowcount > 0: self._dbh.commit() dbc.close() def _count_aliases(self): """Count all alias addresses where the destination address is the address of the Account.""" dbc = self._dbh.cursor() dbc.execute('SELECT COUNT(destination) FROM alias WHERE destination ' '= %s', (str(self._addr),)) a_count = dbc.fetchone()[0] dbc.close() return a_count def _chk_state(self): """Raise an AccountError if the Account is new - not yet saved in the database.""" if self._new: raise AErr(_(u"The account '%s' does not exist.") % self._addr, NO_SUCH_ACCOUNT) @property def address(self): """The Account's EmailAddress instance.""" return self._addr @property def domain(self): """The Domain to which the Account belongs to.""" if self._domain: return self._domain return None @property def gid(self): """The Account's group ID.""" if self._domain: return self._domain.gid return None @property def home(self): """The Account's home directory.""" if not self._new: return '%s/%s' % (self._domain.directory, self._uid) return None @property def mail_location(self): """The Account's MailLocation.""" 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 def set_password(self, password): """Set a password for the new Account. If you want to update the password of an existing Account use Account.modify(). Argument: `password` : basestring The password for the new Account. """ if not self._new: raise AErr(_(u"The account '%s' already exists.") % self._addr, ACCOUNT_EXISTS) if not isinstance(password, basestring) or not password: raise AErr(_(u"Could not accept password: '%s'") % password, 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: raise AErr(_(u"The account '%s' already exists.") % self._addr, ACCOUNT_EXISTS) if not self._passwd: raise AErr(_(u"No password set for account: '%s'") % self._addr, ACCOUNT_MISSING_PASSWORD) self._prepare(MailLocation(self._dbh, mbfmt=cfg_dget('mailbox.format'), directory=cfg_dget('mailbox.root'))) dbc = self._dbh.cursor() qid = ssid = tid = None if self._qlimit: qid = self._qlimit.qid if self._services: ssid = self._services.ssid if self._transport: tid = self._transport.tid dbc.execute('INSERT INTO users (local_part, passwd, uid, gid, mid, ' '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, qid, ssid, tid, # 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._note)) self._dbh.commit() dbc.close() self._new = False def modify(self, field, value): """Update the Account's *field* to the new *value*. Possible values for *field* are: 'name', 'password', 'note'. Arguments: `field` : basestring The attribute name: 'name', 'password' or 'note' `value` : basestring The new value of the attribute. """ if field not in ('name', 'password', 'note'): raise AErr(_(u"Unknown field: '%s'") % field, INVALID_ARGUMENT) self._chk_state() dbc = self._dbh.cursor() if field == 'password': dbc.execute('UPDATE users SET passwd = %s WHERE uid = %s', (pwhash(value, user=self._addr), self._uid)) else: dbc.execute('UPDATE users SET %s = %%s WHERE uid = %%s' % field, (value, self._uid)) if dbc.rowcount > 0: self._dbh.commit() dbc.close() def update_quotalimit(self, quotalimit): """Update the user's quota limit. Arguments: `quotalimit` : VirtualMailManager.quotalimit.QuotaLimit the new quota limit of the domain. """ if cfg_dget('misc.dovecot_version') < 0x10102f00: raise VMMError(_(u'PostgreSQL-based dictionary quota requires ' u'Dovecot >= v1.1.2.'), VMM_ERROR) self._chk_state() if quotalimit == self._qlimit: return self._qlimit = quotalimit if quotalimit is not None: assert isinstance(quotalimit, QuotaLimit) quotalimit = quotalimit.qid self._update_tables('qid', quotalimit) def update_serviceset(self, serviceset): """Assign a different set of services to the Account. Argument: `serviceset` : VirtualMailManager.serviceset.ServiceSet the new service set. """ self._chk_state() if serviceset == self._services: return self._services = serviceset if serviceset is not None: assert isinstance(serviceset, ServiceSet) serviceset = serviceset.ssid self._update_tables('ssid', serviceset) def update_transport(self, transport): """Sets a new transport for the Account. Arguments: `transport` : VirtualMailManager.transport.Transport the new transport """ self._chk_state() if transport == self._transport: return self._transport = transport if transport is not None: assert isinstance(transport, Transport) validate_transport(transport, self._mail) transport = transport.tid self._update_tables('tid', transport) def _get_info_transport(self): if self._transport: return self._transport.transport return format_domain_default(self._domain.transport.transport) def _get_info_serviceset(self): if self._services: services = self._services.services fmt = lambda s: s else: services = self._domain.serviceset.services fmt = format_domain_default ret = {} for service, state in services.iteritems(): # TP: A service (e.g. pop3 or imap) may be enabled/usable or # disabled/unusable for a user. ret[service] = fmt((_('disabled'), _('enabled'))[state]) return ret def get_info(self): """Returns a dict with some information about the Account. The keys of the dict are: 'address', 'gid', 'home', 'imap' 'mail_location', 'name', 'pop3', 'sieve', 'smtp', transport', 'uid', 'uq_bytes', 'uq_messages', 'ql_bytes', 'ql_messages', and 'ql_domaindefault'. """ self._chk_state() dbc = self._dbh.cursor() dbc.execute('SELECT name, CASE WHEN bytes IS NULL THEN 0 ELSE bytes ' 'END, CASE WHEN messages IS NULL THEN 0 ELSE messages END ' 'FROM users LEFT JOIN userquota USING (uid) WHERE ' 'users.uid = %s', (self._uid,)) info = dbc.fetchone() dbc.close() if info: info = dict(zip(('name', 'uq_bytes', 'uq_messages'), info)) info.update(self._get_info_serviceset()) info['address'] = self._addr info['gid'] = self._domain.gid info['home'] = '%s/%s' % (self._domain.directory, self._uid) info['mail_location'] = self._mail.mail_location if self._qlimit: info['ql_bytes'] = self._qlimit.bytes info['ql_messages'] = self._qlimit.messages info['ql_domaindefault'] = False else: info['ql_bytes'] = self._domain.quotalimit.bytes 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‽ raise AErr(_(u"Could not fetch information for account: '%s'") % self._addr, NO_SUCH_ACCOUNT) def get_aliases(self): """Return a list with all alias e-mail addresses, whose destination is the address of the Account.""" self._chk_state() dbc = self._dbh.cursor() dbc.execute("SELECT address ||'@'|| domainname FROM alias, " "domain_name WHERE destination = %s AND domain_name.gid = " "alias.gid AND domain_name.is_primary ORDER BY address", (str(self._addr),)) addresses = dbc.fetchall() dbc.close() aliases = [] if addresses: aliases = [alias[0] for alias in addresses] return aliases def delete(self, force=False): """Delete the Account from the database. Argument: `force` : bool if *force* is `True`, all aliases, which points to the Account, will be also deleted. If there are aliases and *force* is `False`, an AccountError will be raised. """ if not isinstance(force, bool): raise TypeError('force must be a bool') self._chk_state() dbc = self._dbh.cursor() if force: dbc.execute('DELETE FROM users WHERE uid = %s', (self._uid),) # delete also all aliases where the destination address is the same # as for this account. dbc.execute("DELETE FROM alias WHERE destination = %s", (str(self._addr),)) self._dbh.commit() else: # check first for aliases a_count = self._count_aliases() if a_count > 0: dbc.close() raise AErr(_(u"There are %(count)d aliases with the " u"destination address '%(address)s'.") % {'count': a_count, 'address': self._addr}, ALIAS_PRESENT) dbc.execute('DELETE FROM users WHERE uid = %s', (self._uid,)) self._dbh.commit() dbc.close() self._new = True self._uid = 0 self._addr = self._dbh = self._domain = self._passwd = None self._mail = self._qlimit = self._services = self._transport = None def get_account_by_uid(uid, dbh): """Search an Account by its UID. This function returns a dict (keys: 'address', 'gid' and 'uid'), if an Account with the given *uid* exists. Argument: `uid` : long The Account unique ID. `dbh` : pyPgSQL.PgSQL.Connection a database connection for the database access. """ try: uid = long(uid) except ValueError: raise AErr(_(u'UID must be an int/long.'), INVALID_ARGUMENT) if uid < 1: raise AErr(_(u'UID must be greater than 0.'), INVALID_ARGUMENT) dbc = dbh.cursor() dbc.execute("SELECT local_part||'@'|| domain_name.domainname AS address, " "uid, users.gid, note FROM users LEFT JOIN domain_name ON " "(domain_name.gid = users.gid AND is_primary) WHERE uid = %s", (uid,)) info = dbc.fetchone() dbc.close() if not info: raise AErr(_(u"There is no account with the UID: '%d'") % uid, NO_SUCH_ACCOUNT) info = dict(zip(('address', 'uid', 'gid', 'note'), info)) return info del _, cfg_dget vmm-0.6.1/VirtualMailManager/emailaddress.py0000644000175000017500000001243312033032424017236 0ustar pvopvo# -*- coding: UTF-8 -*- # Copyright (c) 2008 - 2012, Pascal Volk # See COPYING for distribution information. """ VirtualMailManager.emailaddress ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Virtual Mail Manager's EmailAddress class to handle e-mail addresses. """ import re from VirtualMailManager.domain import check_domainname, get_gid from VirtualMailManager.constants import \ DOMAIN_NO_NAME, INVALID_ADDRESS, LOCALPART_INVALID, LOCALPART_TOO_LONG, \ DOMAIN_INVALID from VirtualMailManager.errors import DomainError, EmailAddressError as EAErr RE_LOCALPART = re.compile(r"[^\w!#$%&'\*\+-\.\/=?^_`{\|}~]") _ = lambda msg: msg class EmailAddress(object): """Simple class for validated e-mail addresses.""" __slots__ = ('_localpart', '_domainname') def __init__(self, address, _validate=True): """Creates a new instance from the string/unicode ``address``.""" assert isinstance(address, basestring) self._localpart = None self._domainname = None if _validate: self._chk_address(address) @property def localpart(self): """The local-part of the address *local-part@domain*""" return self._localpart @property def domainname(self): """The domain part of the address *local-part@domain*""" return self._domainname def __eq__(self, other): if isinstance(other, self.__class__): return self._localpart == other._localpart and \ self._domainname == other._domainname return NotImplemented def __ne__(self, other): if isinstance(other, self.__class__): return self._localpart != other._localpart or \ self._domainname != other._domainname return NotImplemented def __hash__(self): return hash((self._localpart.lower(), self._domainname.lower())) def __repr__(self): return "EmailAddress('%s@%s')" % (self._localpart, self._domainname) def __str__(self): return '%s@%s' % (self._localpart, self._domainname) def _chk_address(self, address): """Checks if the string ``address`` could be used for an e-mail address. If so, it will assign the corresponding values to the attributes `_localpart` and `_domainname`.""" parts = address.split('@') p_len = len(parts) if p_len < 2: raise EAErr(_(u"Missing the '@' sign in address: '%s'") % address, INVALID_ADDRESS) elif p_len > 2: raise EAErr(_(u"Too many '@' signs in address: '%s'") % address, INVALID_ADDRESS) if not parts[0]: raise EAErr(_(u"Missing local-part in address: '%s'") % address, LOCALPART_INVALID) if not parts[1]: raise EAErr(_(u"Missing domain name in address: '%s'") % address, DOMAIN_NO_NAME) self._localpart = check_localpart(parts[0]) self._domainname = check_domainname(parts[1]) class DestinationEmailAddress(EmailAddress): """Provides additionally the domains group ID - when the domain is known in the database.""" __slots__ = ('_gid', '_localhost') def __init__(self, address, dbh, _validate=False): """Creates a new DestinationEmailAddress instance Arguments: `address`: string/unicode a e-mail address like user@example.com `dbh`: pyPgSQL.PgSQL.Connection/pyPgSQL.PgSQL.connection a database connection for the database access """ super(DestinationEmailAddress, self).__init__(address, _validate) self._localhost = False if not _validate: try: self._chk_address(address) except DomainError, err: if err.code is DOMAIN_INVALID and \ address.split('@')[1] == 'localhost': self._localhost = True self._domainname = 'localhost' else: raise self._gid = 0 if not self._localhost: self._find_domain(dbh) else: self._localpart = self._localpart.lower() def _find_domain(self, dbh): """Checks if the domain is known""" self._gid = get_gid(dbh, self._domainname) if self._gid: self._localpart = self._localpart.lower() @property def at_localhost(self): """True when the address is something@localhost.""" return self._localhost @property def gid(self): """The domains group ID. 0 if the domain is not known.""" return self._gid def check_localpart(localpart): """Returns the validated local-part `localpart`. Throws a `EmailAddressError` if the local-part is too long or contains invalid characters. """ if len(localpart) > 64: raise EAErr(_(u"The local-part '%s' is too long.") % localpart, LOCALPART_TOO_LONG) invalid_chars = set(RE_LOCALPART.findall(localpart)) if invalid_chars: i_chars = u''.join((u'"%s" ' % c for c in invalid_chars)) raise EAErr(_(u"The local-part '%(l_part)s' contains invalid " u"characters: %(i_chars)s") % {'l_part': localpart, 'i_chars': i_chars}, LOCALPART_INVALID) return localpart del _ vmm-0.6.1/VirtualMailManager/common.py0000644000175000017500000002334612033032424016076 0ustar pvopvo# -*- coding: UTF-8 -*- # Copyright (c) 2010 - 2012, Pascal Volk # See COPYING for distribution information. """ VirtualMailManager.common ~~~~~~~~~~~~~~~~~~~~~~~~~ Some common functions """ import locale import os import re import stat from VirtualMailManager import ENCODING from VirtualMailManager.constants import INVALID_MAIL_LOCATION, \ NOT_EXECUTABLE, NO_SUCH_BINARY, TYPE_ACCOUNT, TYPE_ALIAS, TYPE_RELOCATED from VirtualMailManager.errors import VMMError VERSION_RE = re.compile(r'^(\d+)\.(\d+)\.(?:(\d+)|(alpha|beta|rc)(\d+))$') _version_level = dict(alpha=0xA, beta=0xB, rc=0xC) _version_cache = {} _ = lambda msg: msg def expand_path(path): """Expands paths, starting with ``.`` or ``~``, to an absolute path.""" if path.startswith('.'): return os.path.abspath(path) if path.startswith('~'): return os.path.expanduser(path) return path def get_unicode(string): """Converts `string` to `unicode`, if necessary.""" if isinstance(string, unicode): return string return unicode(string, ENCODING, 'replace') def lisdir(path): """Checks if `path` is a directory. Doesn't follow symbolic links. Returns bool. """ try: lstat = os.lstat(path) except OSError: return False return stat.S_ISDIR(lstat.st_mode) def exec_ok(binary): """Checks if the `binary` exists and if it is executable. Throws a `VMMError` if the `binary` isn't a file or is not executable. """ binary = expand_path(binary) if not os.path.isfile(binary): raise VMMError(_(u"No such file: '%s'") % get_unicode(binary), NO_SUCH_BINARY) if not os.access(binary, os.X_OK): raise VMMError(_(u"File is not executable: '%s'") % get_unicode(binary), NOT_EXECUTABLE) return binary def human_size(size): """Converts the `size` in bytes in human readable format.""" if not isinstance(size, (long, int)): try: size = long(size) except ValueError: raise TypeError("'size' must be a positive long or int.") if size < 0: raise ValueError("'size' must be a positive long or int.") if size < 1024: return str(size) # TP: abbreviations of gibibyte, tebibyte kibibyte and mebibyte prefix_multiply = ((_(u'TiB'), 1 << 40), (_(u'GiB'), 1 << 30), (_(u'MiB'), 1 << 20), (_(u'KiB'), 1 << 10)) for prefix, multiply in prefix_multiply: if size >= multiply: # TP: e.g.: '%(size)s %(prefix)s' -> '118.30 MiB' return _(u'%(size)s %(prefix)s') % { 'size': locale.format('%.2f', float(size) / multiply, True).decode(ENCODING, 'replace'), 'prefix': prefix} def size_in_bytes(size): """Converts the string `size` to a long (size in bytes). The string `size` can be suffixed with *b* (bytes), *k* (kilobytes), *M* (megabytes) or *G* (gigabytes). """ if not isinstance(size, basestring) or not size: raise TypeError('size must be a non empty string.') if size[-1].upper() in ('B', 'K', 'M', 'G'): try: num = int(size[:-1]) except ValueError: raise ValueError('Not a valid integer value: %r' % size[:-1]) unit = size[-1].upper() if unit == 'B': return num elif unit == 'K': return num << 10L elif unit == 'M': return num << 20L else: return num << 30L else: try: num = int(size) except ValueError: raise ValueError('Not a valid size value: %r' % size) return num def validate_transport(transport, maillocation): """Checks if the `transport` is usable for the given `maillocation`. Throws a `VMMError` if the chosen `transport` is unable to write messages in the `maillocation`'s mailbox format. Arguments: `transport` : VirtualMailManager.transport.Transport a Transport object `maillocation` : VirtualMailManager.maillocation.MailLocation a MailLocation object """ if transport.transport in ('virtual', 'virtual:') and \ not maillocation.postfix: raise VMMError(_(u"Invalid transport '%(transport)s' for mailbox " u"format '%(mbfmt)s'.") % {'transport': transport.transport, 'mbfmt': maillocation.mbformat}, INVALID_MAIL_LOCATION) def version_hex(version_string): """Converts a Dovecot version, e.g.: '1.2.3' or '2.0.beta4', to an int. Raises a `ValueError` if the *version_string* has the wrong™ format. version_hex('1.2.3') -> 270548736 hex(version_hex('1.2.3')) -> '0x10203f00' """ global _version_cache if version_string in _version_cache: return _version_cache[version_string] version = 0 version_mo = VERSION_RE.match(version_string) if not version_mo: raise ValueError('Invalid version string: %r' % version_string) major, minor, patch, level, serial = version_mo.groups() major = int(major) minor = int(minor) if patch: patch = int(patch) if serial: serial = int(serial) if major > 0xFF or minor > 0xFF or \ patch and patch > 0xFF or serial and serial > 0xFF: raise ValueError('Invalid version string: %r' % version_string) version += major << 28 version += minor << 20 if patch: version += patch << 12 version += _version_level.get(level, 0xF) << 8 if serial: version += serial _version_cache[version_string] = version return version def version_str(version): """Converts a Dovecot version previously converted with version_hex back to a string. Raises a `TypeError` if *version* is not an int/long. Raises a `ValueError` if *version* is an incorrect int version. """ global _version_cache if version in _version_cache: return _version_cache[version] if not isinstance(version, (int, long)): raise TypeError('Argument is not a int/long: %r', version) major = (version >> 28) & 0xFF minor = (version >> 20) & 0xFF patch = (version >> 12) & 0xFF level = (version >> 8) & 0x0F serial = version & 0xFF levels = dict(zip(_version_level.values(), _version_level.keys())) if level == 0xF and not serial: version_string = '%u.%u.%u' % (major, minor, patch) elif level in levels and not patch: version_string = '%u.%u.%s%u' % (major, minor, levels[level], serial) else: raise ValueError('Invalid version: %r' % hex(version)) _version_cache[version] = version_string return version_string def format_domain_default(domaindata): """Format info output when the value displayed is the domain default.""" # TP: [domain default] indicates that a user's setting is the same as # configured in the user's domain. # e.g.: [ 0.84%] 42/5,000 [domain default] return _(u'%s [domain default]') % domaindata def search_addresses(dbh, typelimit=None, lpattern=None, llike=False, dpattern=None, dlike=False): """'Search' for addresses by *pattern* in the database. The search is limited by *typelimit*, a bitfield with values TYPE_ACCOUNT, TYPE_ALIAS, TYPE_RELOCATED, or a bitwise OR thereof. If no limit is specified, all types will be searched. *lpattern* may be a local part or a partial local part - starting and/or ending with a '%' sign. When the *lpattern* starts or ends with a '%' sign *llike* has to be `True` to perform a wildcard search. To retrieve all available addresses use the arguments' default values. *dpattern* and *dlike* behave analogously for the domain part of an address, allowing for separate pattern matching: testuser%@example.% The return value of this function is a tuple. The first element is a list of domain IDs sorted alphabetically by the corresponding domain names. The second element is a dictionary indexed by domain ID, holding lists to associated addresses. Each address is itself actually a tuple of address, type, and boolean indicating whether the address stems from an alias domain. """ if typelimit is None: typelimit = TYPE_ACCOUNT | TYPE_ALIAS | TYPE_RELOCATED queries = [] if typelimit & TYPE_ACCOUNT: queries.append('SELECT gid, local_part, %d AS type FROM users' % TYPE_ACCOUNT) if typelimit & TYPE_ALIAS: queries.append('SELECT DISTINCT gid, address as local_part, ' '%d AS type FROM alias' % TYPE_ALIAS) if typelimit & TYPE_RELOCATED: queries.append('SELECT gid, address as local_part, %d AS type ' 'FROM relocated' % TYPE_RELOCATED) sql = "SELECT gid, local_part || '@' || domainname AS address, " sql += 'type, NOT is_primary AS from_aliasdomain FROM (' sql += ' UNION '.join(queries) sql += ') a JOIN domain_name USING (gid)' nextkw = 'WHERE' sqlargs = [] for like, field, pattern in ((dlike, 'domainname', dpattern), (llike, 'local_part', lpattern)): if like: match = 'LIKE' else: if not pattern: continue match = '=' sql += ' %s %s %s %%s' % (nextkw, field, match) sqlargs.append(pattern) nextkw = 'AND' sql += ' ORDER BY domainname, local_part' dbc = dbh.cursor() dbc.execute(sql, sqlargs) result = dbc.fetchall() dbc.close() gids = [] daddrs = {} for gid, address, addrtype, aliasdomain in result: if gid not in daddrs: gids.append(gid) daddrs[gid] = [] daddrs[gid].append((address, addrtype, aliasdomain)) return gids, daddrs del _ vmm-0.6.1/VirtualMailManager/handler.py0000644000175000017500000011443512033032424016223 0ustar pvopvo# -*- coding: UTF-8 -*- # Copyright (c) 2007 - 2012, Pascal Volk # See COPYING for distribution information. """ VirtualMailManager.handler ~~~~~~~~~~~~~~~~~~~~~~~~~~ A wrapper class. It wraps round all other classes and does some dependencies checks. Additionally it communicates with the PostgreSQL database, creates or deletes directories of domains or users. """ import os import re from shutil import rmtree from subprocess import Popen, PIPE from VirtualMailManager.account import Account from VirtualMailManager.alias import Alias from VirtualMailManager.aliasdomain import AliasDomain from VirtualMailManager.catchall import CatchallAlias from VirtualMailManager.common import exec_ok, lisdir from VirtualMailManager.config import Config as Cfg from VirtualMailManager.constants import MIN_GID, MIN_UID, \ ACCOUNT_EXISTS, ALIAS_EXISTS, CONF_NOFILE, CONF_NOPERM, CONF_WRONGPERM, \ DATABASE_ERROR, DOMAINDIR_GROUP_MISMATCH, DOMAIN_INVALID, \ FOUND_DOTS_IN_PATH, INVALID_ARGUMENT, MAILDIR_PERM_MISMATCH, \ NOT_EXECUTABLE, NO_SUCH_ACCOUNT, NO_SUCH_ALIAS, NO_SUCH_BINARY, \ NO_SUCH_DIRECTORY, NO_SUCH_RELOCATED, RELOCATED_EXISTS, UNKNOWN_SERVICE, \ VMM_ERROR, LOCALPART_INVALID, TYPE_ACCOUNT, TYPE_ALIAS, TYPE_RELOCATED from VirtualMailManager.domain import Domain from VirtualMailManager.emailaddress import DestinationEmailAddress, \ EmailAddress, RE_LOCALPART from VirtualMailManager.errors import \ DomainError, NotRootError, PermissionError, VMMError from VirtualMailManager.mailbox import new as new_mailbox from VirtualMailManager.pycompat import all, any from VirtualMailManager.quotalimit import QuotaLimit from VirtualMailManager.relocated import Relocated from VirtualMailManager.serviceset import ServiceSet, SERVICES from VirtualMailManager.transport import Transport _ = lambda msg: msg _db_mod = None CFG_FILE = 'vmm.cfg' CFG_PATH = '/root:/usr/local/etc:/etc' RE_DOMAIN_SEARCH = """^[a-z0-9-\.]+$""" OTHER_TYPES = { TYPE_ACCOUNT: (_(u'an account'), ACCOUNT_EXISTS), TYPE_ALIAS: (_(u'an alias'), ALIAS_EXISTS), TYPE_RELOCATED: (_(u'a relocated user'), RELOCATED_EXISTS), } class Handler(object): """Wrapper class to simplify the access on all the stuff from VirtualMailManager""" __slots__ = ('_cfg', '_cfg_fname', '_db_connect', '_dbh', '_warnings') def __init__(self, skip_some_checks=False): """Creates a new Handler instance. ``skip_some_checks`` : bool When a derived class knows how to handle all checks this argument may be ``True``. By default it is ``False`` and all checks will be performed. Throws a NotRootError if your uid is greater 0. """ self._cfg_fname = '' self._warnings = [] self._cfg = None self._dbh = None self._db_connect = None if os.geteuid(): raise NotRootError(_(u"You are not root.\n\tGood bye!\n"), CONF_NOPERM) if self._check_cfg_file(): self._cfg = Cfg(self._cfg_fname) self._cfg.load() if not skip_some_checks: self._cfg.check() self._chkenv() self._set_db_connect() def _find_cfg_file(self): """Search the CFG_FILE in CFG_PATH. Raise a VMMError when no vmm.cfg could be found. """ for path in CFG_PATH.split(':'): tmp = os.path.join(path, CFG_FILE) if os.path.isfile(tmp): self._cfg_fname = tmp break if not self._cfg_fname: raise VMMError(_(u"Could not find '%(cfg_file)s' in: " u"'%(cfg_path)s'") % {'cfg_file': CFG_FILE, 'cfg_path': CFG_PATH}, CONF_NOFILE) def _check_cfg_file(self): """Checks the configuration file, returns bool""" self._find_cfg_file() fstat = os.stat(self._cfg_fname) fmode = int(oct(fstat.st_mode & 0777)) if fmode % 100 and fstat.st_uid != fstat.st_gid or \ fmode % 10 and fstat.st_uid == fstat.st_gid: # TP: Please keep the backticks around the command. `chmod 0600 …` raise PermissionError(_(u"wrong permissions for '%(file)s': " u"%(perms)s\n`chmod 0600 %(file)s` would " u"be great.") % {'file': self._cfg_fname, 'perms': fmode}, CONF_WRONGPERM) else: return True def _chkenv(self): """Make sure our base_directory is a directory and that all required executables exists and are executable. If not, a VMMError will be raised""" dir_created = False basedir = self._cfg.dget('misc.base_directory') if not os.path.exists(basedir): old_umask = os.umask(0006) os.makedirs(basedir, 0771) os.chown(basedir, 0, 0) os.umask(old_umask) dir_created = True if not dir_created and not lisdir(basedir): raise VMMError(_(u"'%(path)s' is not a directory.\n(%(cfg_file)s: " u"section 'misc', option 'base_directory')") % {'path': basedir, 'cfg_file': self._cfg_fname}, NO_SUCH_DIRECTORY) for opt, val in self._cfg.items('bin'): try: exec_ok(val) except VMMError, err: if err.code in (NO_SUCH_BINARY, NOT_EXECUTABLE): raise VMMError(err.msg + _(u"\n(%(cfg_file)s: section " u"'bin', option '%(option)s')") % {'cfg_file': self._cfg_fname, 'option': opt}, err.code) else: raise def _set_db_connect(self): """check which module to use and set self._db_connect""" global _db_mod if self._cfg.dget('database.module').lower() == 'psycopg2': try: _db_mod = __import__('psycopg2') except ImportError: raise VMMError(_(u"Unable to import database module '%s'.") % 'psycopg2', VMM_ERROR) self._db_connect = self._psycopg2_connect else: try: tmp = __import__('pyPgSQL', globals(), locals(), ['PgSQL']) except ImportError: raise VMMError(_(u"Unable to import database module '%s'.") % 'pyPgSQL', VMM_ERROR) _db_mod = tmp.PgSQL self._db_connect = self._pypgsql_connect def _pypgsql_connect(self): """Creates a pyPgSQL.PgSQL.connection instance.""" if self._dbh is None or (isinstance(self._dbh, _db_mod.Connection) and not self._dbh._isOpen): try: self._dbh = _db_mod.connect( database=self._cfg.dget('database.name'), user=self._cfg.pget('database.user'), host=self._cfg.dget('database.host'), port=self._cfg.dget('database.port'), password=self._cfg.pget('database.pass'), client_encoding='utf8', unicode_results=True) dbc = self._dbh.cursor() dbc.execute("SET NAMES 'UTF8'") dbc.close() except _db_mod.libpq.DatabaseError, err: raise VMMError(str(err), DATABASE_ERROR) def _psycopg2_connect(self): """Return a new psycopg2 connection object.""" if self._dbh is None or \ (isinstance(self._dbh, _db_mod.extensions.connection) and self._dbh.closed): try: self._dbh = _db_mod.connect( host=self._cfg.dget('database.host'), sslmode=self._cfg.dget('database.sslmode'), port=self._cfg.dget('database.port'), database=self._cfg.dget('database.name'), user=self._cfg.pget('database.user'), password=self._cfg.pget('database.pass')) self._dbh.set_client_encoding('utf8') _db_mod.extensions.register_type(_db_mod.extensions.UNICODE) dbc = self._dbh.cursor() dbc.execute("SET NAMES 'UTF8'") dbc.close() except _db_mod.DatabaseError, err: raise VMMError(str(err), DATABASE_ERROR) def _chk_other_address_types(self, address, exclude): """Checks if the EmailAddress *address* is known as `TYPE_ACCOUNT`, `TYPE_ALIAS` or `TYPE_RELOCATED`, but not as the `TYPE_*` specified by *exclude*. If the *address* is known as one of the `TYPE_*`s the according `TYPE_*` constant will be returned. Otherwise 0 will be returned.""" assert exclude in (TYPE_ACCOUNT, TYPE_ALIAS, TYPE_RELOCATED) and \ isinstance(address, EmailAddress) if exclude is not TYPE_ACCOUNT: account = Account(self._dbh, address) if account: return TYPE_ACCOUNT if exclude is not TYPE_ALIAS: alias = Alias(self._dbh, address) if alias: return TYPE_ALIAS if exclude is not TYPE_RELOCATED: relocated = Relocated(self._dbh, address) if relocated: return TYPE_RELOCATED return 0 def _is_other_address(self, address, exclude): """Checks if *address* is known for an Account (TYPE_ACCOUNT), Alias (TYPE_ALIAS) or Relocated (TYPE_RELOCATED), except for *exclude*. Returns `False` if the address is not known for other types. Raises a `VMMError` if the address is known. """ other = self._chk_other_address_types(address, exclude) if not other: return False # TP: %(a_type)s will be one of: 'an account', 'an alias' or # 'a relocated user' msg = _(u"There is already %(a_type)s with the address '%(address)s'.") raise VMMError(msg % {'a_type': OTHER_TYPES[other][0], 'address': address}, OTHER_TYPES[other][1]) def _get_account(self, address): """Return an Account instances for the given address (str).""" address = EmailAddress(address) self._db_connect() return Account(self._dbh, address) def _get_alias(self, address): """Return an Alias instances for the given address (str).""" address = EmailAddress(address) self._db_connect() return Alias(self._dbh, address) def _get_catchall(self, domain): """Return a CatchallAlias instances for the given domain (str).""" self._db_connect() return CatchallAlias(self._dbh, domain) def _get_relocated(self, address): """Return a Relocated instances for the given address (str).""" address = EmailAddress(address) self._db_connect() return Relocated(self._dbh, address) def _get_domain(self, domainname): """Return a Domain instances for the given domain name (str).""" self._db_connect() return Domain(self._dbh, domainname) def _get_disk_usage(self, directory): """Estimate file space usage for the given directory. Arguments: `directory` : basestring The directory to summarize recursively disk usage for """ if lisdir(directory): return Popen([self._cfg.dget('bin.du'), "-hs", directory], stdout=PIPE).communicate()[0].split('\t')[0] else: self._warnings.append(_('No such directory: %s') % directory) return 0 def _make_domain_dir(self, domain): """Create a directory for the `domain` and its accounts.""" cwd = os.getcwd() hashdir, domdir = domain.directory.split(os.path.sep)[-2:] dir_created = False os.chdir(self._cfg.dget('misc.base_directory')) old_umask = os.umask(0022) if not os.path.exists(hashdir): os.mkdir(hashdir, 0711) os.chown(hashdir, 0, 0) dir_created = True if not dir_created and not lisdir(hashdir): raise VMMError(_(u"'%s' is not a directory.") % hashdir, NO_SUCH_DIRECTORY) if os.path.exists(domain.directory): raise VMMError(_(u"The file/directory '%s' already exists.") % domain.directory, VMM_ERROR) os.mkdir(os.path.join(hashdir, domdir), self._cfg.dget('domain.directory_mode')) os.chown(domain.directory, 0, domain.gid) os.umask(old_umask) os.chdir(cwd) def _make_home(self, account): """Create a home directory for the new Account *account*.""" domdir = account.domain.directory if not lisdir(domdir): self._make_domain_dir(account.domain) os.umask(0007) uid = account.uid os.chdir(domdir) os.mkdir('%s' % uid, self._cfg.dget('account.directory_mode')) os.chown('%s' % uid, uid, account.gid) def _make_account_dirs(self, account): """Create all necessary directories for the account.""" oldpwd = os.getcwd() self._make_home(account) mailbox = new_mailbox(account) mailbox.create() folders = self._cfg.dget('mailbox.folders').split(':') if any(folders): bad = mailbox.add_boxes(folders, self._cfg.dget('mailbox.subscribe')) if bad: self._warnings.append(_(u"Skipped mailbox folders:") + '\n\t- ' + '\n\t- '.join(bad)) os.chdir(oldpwd) def _delete_home(self, domdir, uid, gid): """Delete a user's home directory. Arguments: `domdir` : basestring The directory of the domain the user belongs to (commonly AccountObj.domain.directory) `uid` : int/long The user's UID (commonly AccountObj.uid) `gid` : int/long The user's GID (commonly AccountObj.gid) """ assert all(isinstance(xid, (long, int)) for xid in (uid, gid)) and \ isinstance(domdir, basestring) if uid < MIN_UID or gid < MIN_GID: raise VMMError(_(u"UID '%(uid)u' and/or GID '%(gid)u' are less " u"than %(min_uid)u/%(min_gid)u.") % {'uid': uid, 'gid': gid, 'min_gid': MIN_GID, 'min_uid': MIN_UID}, MAILDIR_PERM_MISMATCH) if domdir.count('..'): raise VMMError(_(u'Found ".." in domain directory path: %s') % domdir, FOUND_DOTS_IN_PATH) if not lisdir(domdir): raise VMMError(_(u"No such directory: %s") % domdir, NO_SUCH_DIRECTORY) os.chdir(domdir) userdir = '%s' % uid if not lisdir(userdir): self._warnings.append(_(u"No such directory: %s") % os.path.join(domdir, userdir)) return mdstat = os.lstat(userdir) if (mdstat.st_uid, mdstat.st_gid) != (uid, gid): raise VMMError(_(u'Detected owner/group mismatch in home ' u'directory.'), MAILDIR_PERM_MISMATCH) rmtree(userdir, ignore_errors=True) def _delete_domain_dir(self, domdir, gid): """Delete a domain's directory. Arguments: `domdir` : basestring The domain's directory (commonly DomainObj.directory) `gid` : int/long The domain's GID (commonly DomainObj.gid) """ assert isinstance(domdir, basestring) and isinstance(gid, (long, int)) if gid < MIN_GID: raise VMMError(_(u"GID '%(gid)u' is less than '%(min_gid)u'.") % {'gid': gid, 'min_gid': MIN_GID}, DOMAINDIR_GROUP_MISMATCH) if domdir.count('..'): raise VMMError(_(u'Found ".." in domain directory path: %s') % domdir, FOUND_DOTS_IN_PATH) if not lisdir(domdir): self._warnings.append(_('No such directory: %s') % domdir) return dirst = os.lstat(domdir) if dirst.st_gid != gid: raise VMMError(_(u'Detected group mismatch in domain directory: ' u'%s') % domdir, DOMAINDIR_GROUP_MISMATCH) rmtree(domdir, ignore_errors=True) def has_warnings(self): """Checks if warnings are present, returns bool.""" return bool(len(self._warnings)) def get_warnings(self): """Returns a list with all available warnings and resets all warnings. """ ret_val = self._warnings[:] del self._warnings[:] return ret_val def cfg_dget(self, option): """Get the configured value of the *option* (section.option). When the option was not configured its default value will be returned.""" return self._cfg.dget(option) def cfg_pget(self, option): """Get the configured value of the *option* (section.option).""" return self._cfg.pget(option) def cfg_install(self): """Installs the cfg_dget method as ``cfg_dget`` into the built-in namespace.""" import __builtin__ assert 'cfg_dget' not in __builtin__.__dict__ __builtin__.__dict__['cfg_dget'] = self._cfg.dget def domain_add(self, domainname, transport=None): """Wrapper around Domain's set_quotalimit, set_transport and save.""" dom = self._get_domain(domainname) if transport is None: dom.set_transport(Transport(self._dbh, transport=self._cfg.dget('domain.transport'))) else: dom.set_transport(Transport(self._dbh, transport=transport)) dom.set_quotalimit(QuotaLimit(self._dbh, bytes=long(self._cfg.dget('domain.quota_bytes')), messages=self._cfg.dget('domain.quota_messages'))) dom.set_serviceset(ServiceSet(self._dbh, imap=self._cfg.dget('domain.imap'), pop3=self._cfg.dget('domain.pop3'), sieve=self._cfg.dget('domain.sieve'), smtp=self._cfg.dget('domain.smtp'))) dom.set_directory(self._cfg.dget('misc.base_directory')) dom.save() self._make_domain_dir(dom) def domain_quotalimit(self, domainname, bytes_, messages=0, force=None): """Wrapper around Domain.update_quotalimit().""" if not all(isinstance(i, (int, long)) for i in (bytes_, messages)): raise TypeError("'bytes_' and 'messages' have to be " "integers or longs.") if force is not None and force != 'force': raise DomainError(_(u"Invalid argument: '%s'") % force, INVALID_ARGUMENT) dom = self._get_domain(domainname) quotalimit = QuotaLimit(self._dbh, bytes=bytes_, messages=messages) if force is None: dom.update_quotalimit(quotalimit) else: dom.update_quotalimit(quotalimit, force=True) def domain_services(self, domainname, force=None, *services): """Wrapper around Domain.update_serviceset().""" kwargs = dict.fromkeys(SERVICES, False) if force is not None and force != 'force': raise DomainError(_(u"Invalid argument: '%s'") % force, INVALID_ARGUMENT) for service in set(services): if service not in SERVICES: raise DomainError(_(u"Unknown service: '%s'") % service, UNKNOWN_SERVICE) kwargs[service] = True dom = self._get_domain(domainname) serviceset = ServiceSet(self._dbh, **kwargs) dom.update_serviceset(serviceset, (True, False)[not force]) def domain_transport(self, domainname, transport, force=None): """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) def domain_note(self, domainname, note): """Wrapper around Domain.update_note()""" dom = self._get_domain(domainname) dom.update_note(note) def domain_delete(self, domainname, force=False): """Wrapper around Domain.delete()""" if not isinstance(force, bool): raise TypeError('force must be a bool') dom = self._get_domain(domainname) gid = dom.gid domdir = dom.directory if self._cfg.dget('domain.force_deletion') or force: dom.delete(True) else: dom.delete(False) if self._cfg.dget('domain.delete_directory'): self._delete_domain_dir(domdir, gid) def domain_info(self, domainname, details=None): """Wrapper around Domain.get_info(), Domain.get_accounts(), Domain.get_aliase_names(), Domain.get_aliases() and Domain.get_relocated.""" if details not in [None, 'accounts', 'aliasdomains', 'aliases', 'full', 'relocated', 'catchall']: raise VMMError(_(u"Invalid argument: '%s'") % details, INVALID_ARGUMENT) dom = self._get_domain(domainname) dominfo = dom.get_info() if dominfo['domain name'].startswith('xn--'): dominfo['domain name'] += ' (%s)' % \ dominfo['domain name'].decode('idna') if details is None: return dominfo elif details == 'accounts': return (dominfo, dom.get_accounts()) elif details == 'aliasdomains': return (dominfo, dom.get_aliase_names()) elif details == 'aliases': return (dominfo, dom.get_aliases()) elif details == 'relocated': return(dominfo, dom.get_relocated()) elif details == 'catchall': return(dominfo, dom.get_catchall()) else: return (dominfo, dom.get_aliase_names(), dom.get_accounts(), dom.get_aliases(), dom.get_relocated(), dom.get_catchall()) def aliasdomain_add(self, aliasname, domainname): """Adds an alias domain to the domain. Arguments: `aliasname` : basestring The name of the alias domain `domainname` : basestring The name of the target domain """ dom = self._get_domain(domainname) alias_dom = AliasDomain(self._dbh, aliasname) alias_dom.set_destination(dom) alias_dom.save() def aliasdomain_info(self, aliasname): """Returns a dict (keys: "alias" and "domain") with the names of the alias domain and its primary domain.""" self._db_connect() alias_dom = AliasDomain(self._dbh, aliasname) return alias_dom.info() def aliasdomain_switch(self, aliasname, domainname): """Modifies the target domain of an existing alias domain. Arguments: `aliasname` : basestring The name of the alias domain `domainname` : basestring The name of the new target domain """ dom = self._get_domain(domainname) alias_dom = AliasDomain(self._dbh, aliasname) alias_dom.set_destination(dom) alias_dom.switch() def aliasdomain_delete(self, aliasname): """Deletes the given alias domain. Argument: `aliasname` : basestring The name of the alias domain """ self._db_connect() alias_dom = AliasDomain(self._dbh, aliasname) alias_dom.delete() def domain_list(self, pattern=None): """Wrapper around function search() from module Domain.""" from VirtualMailManager.domain import search like = False if pattern and (pattern.startswith('%') or pattern.endswith('%')): like = True if not re.match(RE_DOMAIN_SEARCH, pattern.strip('%')): raise VMMError(_(u"The pattern '%s' contains invalid " u"characters.") % pattern, DOMAIN_INVALID) self._db_connect() return search(self._dbh, pattern=pattern, like=like) def address_list(self, typelimit, pattern=None): """TODO""" llike = dlike = False lpattern = dpattern = None if pattern: parts = pattern.split('@', 2) if len(parts) == 2: # The pattern includes '@', so let's treat the # parts separately to allow for pattern search like %@domain.% lpattern = parts[0] llike = lpattern.startswith('%') or lpattern.endswith('%') dpattern = parts[1] dlike = dpattern.startswith('%') or dpattern.endswith('%') if llike: checkp = lpattern.strip('%') else: checkp = lpattern if len(checkp) > 0 and re.search(RE_LOCALPART, checkp): raise VMMError(_(u"The pattern '%s' contains invalid " u"characters.") % pattern, LOCALPART_INVALID) else: # else just match on domains # (or should that be local part, I don't know…) dpattern = parts[0] dlike = dpattern.startswith('%') or dpattern.endswith('%') if dlike: checkp = dpattern.strip('%') else: checkp = dpattern if len(checkp) > 0 and not re.match(RE_DOMAIN_SEARCH, checkp): raise VMMError(_(u"The pattern '%s' contains invalid " u"characters.") % pattern, DOMAIN_INVALID) self._db_connect() from VirtualMailManager.common import search_addresses return search_addresses(self._dbh, typelimit=typelimit, lpattern=lpattern, llike=llike, dpattern=dpattern, dlike=dlike) def user_add(self, emailaddress, password): """Wrapper around Account.set_password() and Account.save().""" acc = self._get_account(emailaddress) if acc: raise VMMError(_(u"The account '%s' already exists.") % acc.address, ACCOUNT_EXISTS) self._is_other_address(acc.address, TYPE_ACCOUNT) acc.set_password(password) acc.save() self._make_account_dirs(acc) def alias_add(self, aliasaddress, *targetaddresses): """Creates a new `Alias` entry for the given *aliasaddress* with the given *targetaddresses*.""" alias = self._get_alias(aliasaddress) if not alias: self._is_other_address(alias.address, TYPE_ALIAS) destinations = [DestinationEmailAddress(addr, self._dbh) for addr in targetaddresses] warnings = [] destinations = alias.add_destinations(destinations, warnings) if warnings: self._warnings.append(_('Ignored destination addresses:')) self._warnings.extend((' * %s' % w for w in warnings)) for destination in destinations: if destination.gid and \ not self._chk_other_address_types(destination, TYPE_RELOCATED): self._warnings.append(_(u"The destination account/alias '%s' " u"does not exist.") % destination) def user_delete(self, emailaddress, force=False): """Wrapper around Account.delete(...)""" if not isinstance(force, bool): raise TypeError('force must be a bool') acc = self._get_account(emailaddress) if not acc: raise VMMError(_(u"The account '%s' does not exist.") % acc.address, NO_SUCH_ACCOUNT) uid = acc.uid gid = acc.gid dom_dir = acc.domain.directory acc_dir = acc.home acc.delete(force) if self._cfg.dget('account.delete_directory'): try: self._delete_home(dom_dir, uid, gid) except VMMError, err: if err.code in (FOUND_DOTS_IN_PATH, MAILDIR_PERM_MISMATCH, NO_SUCH_DIRECTORY): warning = _(u"""\ The account has been successfully deleted from the database. But an error occurred while deleting the following directory: '%(directory)s' Reason: %(reason)s""") % {'directory': acc_dir, 'reason': err.msg} self._warnings.append(warning) else: raise def alias_info(self, aliasaddress): """Returns an iterator object for all destinations (`EmailAddress` instances) for the `Alias` with the given *aliasaddress*.""" alias = self._get_alias(aliasaddress) if alias: return alias.get_destinations() if not self._is_other_address(alias.address, TYPE_ALIAS): raise VMMError(_(u"The alias '%s' does not exist.") % alias.address, NO_SUCH_ALIAS) def alias_delete(self, aliasaddress, targetaddresses=None): """Deletes the `Alias` *aliasaddress* with all its destinations from the database. If *targetaddresses* is not ``None``, only the given destinations will be removed from the alias.""" alias = self._get_alias(aliasaddress) error = None if targetaddresses is None: alias.delete() else: destinations = [DestinationEmailAddress(addr, self._dbh) for addr in targetaddresses] warnings = [] try: alias.del_destinations(destinations, warnings) except VMMError, err: error = err if warnings: self._warnings.append(_('Ignored destination addresses:')) self._warnings.extend((' * %s' % w for w in warnings)) if error: raise error def catchall_add(self, domain, *targetaddresses): """Creates a new `CatchallAlias` entry for the given *domain* with the given *targetaddresses*.""" catchall = self._get_catchall(domain) destinations = [DestinationEmailAddress(addr, self._dbh) for addr in targetaddresses] warnings = [] destinations = catchall.add_destinations(destinations, warnings) if warnings: self._warnings.append(_('Ignored destination addresses:')) self._warnings.extend((' * %s' % w for w in warnings)) for destination in destinations: if destination.gid and \ not self._chk_other_address_types(destination, TYPE_RELOCATED): self._warnings.append(_(u"The destination account/alias '%s' " u"does not exist.") % destination) def catchall_info(self, domain): """Returns an iterator object for all destinations (`EmailAddress` instances) for the `CatchallAlias` with the given *domain*.""" return self._get_catchall(domain).get_destinations() def catchall_delete(self, domain, targetaddresses=None): """Deletes the `CatchallAlias` for domain *domain* with all its destinations from the database. If *targetaddresses* is not ``None``, only those destinations will be removed from the alias.""" catchall = self._get_catchall(domain) error = None if targetaddresses is None: catchall.delete() else: destinations = [DestinationEmailAddress(addr, self._dbh) for addr in targetaddresses] warnings = [] try: catchall.del_destinations(destinations, warnings) except VMMError, err: error = err if warnings: self._warnings.append(_('Ignored destination addresses:')) self._warnings.extend((' * %s' % w for w in warnings)) if error: raise error def user_info(self, emailaddress, details=None): """Wrapper around Account.get_info(...)""" if details not in (None, 'du', 'aliases', 'full'): raise VMMError(_(u"Invalid argument: '%s'") % details, INVALID_ARGUMENT) acc = self._get_account(emailaddress) if not acc: if not self._is_other_address(acc.address, TYPE_ACCOUNT): raise VMMError(_(u"The account '%s' does not exist.") % acc.address, NO_SUCH_ACCOUNT) info = acc.get_info() if self._cfg.dget('account.disk_usage') or details in ('du', 'full'): path = os.path.join(acc.home, acc.mail_location.directory) info['disk usage'] = self._get_disk_usage(path) if details in (None, 'du'): return info if details in ('aliases', 'full'): return (info, acc.get_aliases()) return info def user_by_uid(self, uid): """Search for an Account by its *uid*. Returns a dict (address, uid and gid) if a user could be found.""" from VirtualMailManager.account import get_account_by_uid self._db_connect() return get_account_by_uid(uid, self._dbh) def user_password(self, emailaddress, password): """Wrapper for Account.modify('password' ...).""" if not isinstance(password, basestring) or not password: raise VMMError(_(u"Could not accept password: '%s'") % password, INVALID_ARGUMENT) acc = self._get_account(emailaddress) if not acc: raise VMMError(_(u"The account '%s' does not exist.") % acc.address, NO_SUCH_ACCOUNT) acc.modify('password', password) def user_name(self, emailaddress, name): """Wrapper for Account.modify('name', ...).""" acc = self._get_account(emailaddress) if not acc: raise VMMError(_(u"The account '%s' does not exist.") % 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) if not acc: raise VMMError(_(u"The account '%s' does not exist.") % acc.address, NO_SUCH_ACCOUNT) if bytes_ == 'domain': quotalimit = None else: if not all(isinstance(i, (int, long)) for i in (bytes_, messages)): raise TypeError("'bytes_' and 'messages' have to be " "integers or longs.") quotalimit = QuotaLimit(self._dbh, bytes=bytes_, messages=messages) acc.update_quotalimit(quotalimit) def user_transport(self, emailaddress, transport): """Wrapper for Account.update_transport(Transport).""" if not isinstance(transport, basestring) or not transport: raise VMMError(_(u"Could not accept transport: '%s'") % transport, INVALID_ARGUMENT) acc = self._get_account(emailaddress) if not acc: raise VMMError(_(u"The account '%s' does not exist.") % acc.address, NO_SUCH_ACCOUNT) if transport == 'domain': transport = None else: transport = Transport(self._dbh, transport=transport) acc.update_transport(transport) def user_services(self, emailaddress, *services): """Wrapper around Account.update_serviceset().""" acc = self._get_account(emailaddress) if not acc: raise VMMError(_(u"The account '%s' does not exist.") % acc.address, NO_SUCH_ACCOUNT) if len(services) == 1 and services[0] == 'domain': serviceset = None else: kwargs = dict.fromkeys(SERVICES, False) for service in set(services): if service not in SERVICES: raise VMMError(_(u"Unknown service: '%s'") % service, UNKNOWN_SERVICE) kwargs[service] = True serviceset = ServiceSet(self._dbh, **kwargs) acc.update_serviceset(serviceset) def relocated_add(self, emailaddress, targetaddress): """Creates a new `Relocated` entry in the database. If there is already a relocated user with the given *emailaddress*, only the *targetaddress* for the relocated user will be updated.""" relocated = self._get_relocated(emailaddress) if not relocated: self._is_other_address(relocated.address, TYPE_RELOCATED) destination = DestinationEmailAddress(targetaddress, self._dbh) relocated.set_destination(destination) if destination.gid and \ not self._chk_other_address_types(destination, TYPE_RELOCATED): self._warnings.append(_(u"The destination account/alias '%s' " u"does not exist.") % destination) def relocated_info(self, emailaddress): """Returns the target address of the relocated user with the given *emailaddress*.""" relocated = self._get_relocated(emailaddress) if relocated: return relocated.get_info() if not self._is_other_address(relocated.address, TYPE_RELOCATED): raise VMMError(_(u"The relocated user '%s' does not exist.") % relocated.address, NO_SUCH_RELOCATED) def relocated_delete(self, emailaddress): """Deletes the relocated user with the given *emailaddress* from the database.""" relocated = self._get_relocated(emailaddress) relocated.delete() del _ vmm-0.6.1/VirtualMailManager/network.py0000644000175000017500000000701212033032424016267 0ustar pvopvo# -*- coding: UTF-8 -*- # Copyright (c) 2011 - 2012, Pascal Volk # See COPYING for distribution information. """ VirtualMailManager.network ~~~~~~~~~~~~~~~~~~~~~~~~~~ Network/IP address related class and function """ import socket class NetInfo(object): """Simple class for CIDR network addresses an IP addresses.""" __slots__ = ('_addr', '_prefix', '_bits_max', '_family', '_nw_addr') def __init__(self, nw_address): """Creates a new `NetInfo` instance. Argument: `nw_address` : basestring string representation of an IPv4/IPv6 address or network address. E.g. 192.0.2.13, 192.0.2.0/24, 2001:db8::/32 or ::1 When the address has no netmask the prefix length will be set to 32 for IPv4 addresses and 128 for IPv6 addresses. """ self._addr = None self._prefix = 0 self._bits_max = 0 self._family = 0 self._nw_addr = nw_address self._parse_net_range() def __hash__(self): return hash((self._addr, self._family, self._prefix)) def __repr__(self): return "NetInfo('%s')" % self._nw_addr def _parse_net_range(self): """Parse the network range of `self._nw_addr and assign values to the class attributes. `""" sep = '/' if self._nw_addr.count(sep): ip_address, sep, self._prefix = self._nw_addr.partition(sep) self._family, self._addr = get_ip_addr_info(ip_address) else: self._family, self._addr = get_ip_addr_info(self._nw_addr) self._bits_max = (128, 32)[self._family is socket.AF_INET] if self._prefix is 0: self._prefix = self._bits_max else: try: self._prefix = int(self._prefix) except ValueError: raise ValueError('Invalid prefix length: %r' % self._prefix) if self._prefix > self._bits_max or self._prefix < 0: raise ValueError('Invalid prefix length: %r' % self._prefix) @property def family(self): """Address family: `socket.AF_INET` or `socket.AF_INET6`""" return self._family def address_in_net(self, ip_address): """Checks if the `ip_address` belongs to the same subnet.""" family, address = get_ip_addr_info(ip_address) if family != self._family: return False return address >> self._bits_max - self._prefix == \ self._addr >> self._bits_max - self._prefix def get_ip_addr_info(ip_address): """Checks if the string `ip_address` is a valid IPv4 or IPv6 address. When the `ip_address` could be validated successfully a tuple `(address_family, address_as_long)` will be returned. The `address_family`will be either `socket.AF_INET` or `socket.AF_INET6`. """ if not isinstance(ip_address, basestring) or not ip_address: raise TypeError('ip_address must be a non empty string.') if not ip_address.count(':'): family = socket.AF_INET try: address = socket.inet_aton(ip_address) except socket.error: raise ValueError('Not a valid IPv4 address: %r' % ip_address) elif not socket.has_ipv6: raise ValueError('Unsupported IP address (IPv6): %r' % ip_address) else: family = socket.AF_INET6 try: address = socket.inet_pton(family, ip_address) except socket.error: raise ValueError('Not a valid IPv6 address: %r' % ip_address) return (family, long(address.encode('hex'), 16)) vmm-0.6.1/VirtualMailManager/config.py0000644000175000017500000004755512033032424016063 0ustar pvopvo# -*- coding: UTF-8 -*- # Copyright (c) 2007 - 2012, Pascal Volk # See COPYING for distribution information. """ VirtualMailManager.config ~~~~~~~~~~~~~~~~~~~~~~~~~ VMM's configuration module for simplified configuration access. """ from ConfigParser import \ Error, MissingSectionHeaderError, NoOptionError, NoSectionError, \ ParsingError, RawConfigParser from cStringIO import StringIO from VirtualMailManager.common import VERSION_RE, \ exec_ok, expand_path, get_unicode, lisdir, size_in_bytes, version_hex from VirtualMailManager.constants import CONF_ERROR from VirtualMailManager.errors import ConfigError, VMMError from VirtualMailManager.maillocation import known_format from VirtualMailManager.password import verify_scheme as _verify_scheme DB_MODULES = ('psycopg2', 'pypgsql') DB_SSL_MODES = ('allow', 'disabled', 'prefer', 'require', 'verify-ca', 'verify-full') _ = lambda msg: msg class BadOptionError(Error): """Raised when a option isn't in the format 'section.option'.""" pass class ConfigValueError(Error): """Raised when creating or validating of new values fails.""" pass class NoDefaultError(Error): """Raised when the requested option has no default value.""" def __init__(self, section, option): Error.__init__(self, 'Option %r in section %r has no default value' % (option, section)) class LazyConfig(RawConfigParser): """The **lazy** derivate of the `RawConfigParser`. There are two additional getters: `pget()` The polymorphic getter, which returns a option's value with the appropriate type. `dget()` Like `LazyConfig.pget()`, but returns the option's default, from `LazyConfig._cfg['sectionname']['optionname'].default`, if the option is not configured in a ini-like configuration file. `set()` differs from `RawConfigParser`'s `set()` method. `set()` takes the `section` and `option` arguments combined to a single string in the form "section.option". """ def __init__(self): RawConfigParser.__init__(self) self._modified = False # sample _cfg dict. Create your own in your derived class. self._cfg = { 'sectionname': { 'optionname': LazyConfigOption(int, 1, self.getint), } } def bool_new(self, value): """Converts the string `value` into a `bool` and returns it. | '1', 'on', 'yes' and 'true' will become `True` | '0', 'off', 'no' and 'false' will become `False` Throws a `ConfigValueError` for all other values, except bools. """ if isinstance(value, bool): return value if value.lower() in self._boolean_states: return self._boolean_states[value.lower()] else: raise ConfigValueError(_(u"Not a boolean: '%s'") % get_unicode(value)) def getboolean(self, section, option): """Returns the boolean value of the option, in the given section. For a boolean True, the value must be set to '1', 'on', 'yes', 'true' or True. For a boolean False, the value must set to '0', 'off', 'no', 'false' or False. If the option has another value assigned this method will raise a ValueError. """ # if the setting was modified it may be still a boolean value lets see tmp = self.get(section, option) if isinstance(tmp, bool): return tmp if not tmp.lower() in self._boolean_states: raise ValueError('Not a boolean: %s' % tmp) return self._boolean_states[tmp.lower()] def _get_section_option(self, section_option): """splits ``section_option`` (section.option) in two parts and returns them as list ``[section, option]``, if: * it likes the format of ``section_option`` * the ``section`` is known * the ``option`` is known Else one of the following exceptions will be thrown: * `BadOptionError` * `NoSectionError` * `NoOptionError` """ sect_opt = section_option.lower().split('.') # TODO: cache it if len(sect_opt) != 2 or not sect_opt[0] or not sect_opt[1]: raise BadOptionError(_(u"Bad format: '%s' - expected: " u"section.option") % get_unicode(section_option)) if not sect_opt[0] in self._cfg: raise NoSectionError(sect_opt[0]) if not sect_opt[1] in self._cfg[sect_opt[0]]: raise NoOptionError(sect_opt[1], sect_opt[0]) return sect_opt def items(self, section): """returns an iterable that returns key, value ``tuples`` from the given ``section``. """ if section in self._sections: # check if the section was parsed sect = self._sections[section] elif not section in self._cfg: raise NoSectionError(section) else: return ((k, self._cfg[section][k].default) for k in self._cfg[section].iterkeys()) # still here? Get defaults and merge defaults with configured setting defaults = dict((k, self._cfg[section][k].default) for k in self._cfg[section].iterkeys()) defaults.update(sect) if '__name__' in defaults: del defaults['__name__'] return defaults.iteritems() def dget(self, option): """Returns the value of the `option`. If the option could not be found in the configuration file, the configured default value, from ``LazyConfig._cfg`` will be returned. Arguments: `option` : string the configuration option in the form "section.option" Throws a `NoDefaultError`, if no default value was passed to `LazyConfigOption.__init__()` for the `option`. """ section, option = self._get_section_option(option) try: return self._cfg[section][option].getter(section, option) except (NoSectionError, NoOptionError): if not self._cfg[section][option].default is None: # may be False return self._cfg[section][option].default else: raise NoDefaultError(section, option) def pget(self, option): """Returns the value of the `option`.""" section, option = self._get_section_option(option) return self._cfg[section][option].getter(section, option) def set(self, option, value): """Set the `value` of the `option`. Throws a `ValueError` if `value` couldn't be converted using `LazyConfigOption.cls`. """ # pylint: disable=W0221 # @pylint: _L A Z Y_ section, option = self._get_section_option(option) val = self._cfg[section][option].cls(value) if self._cfg[section][option].validate: val = self._cfg[section][option].validate(val) if not RawConfigParser.has_section(self, section): self.add_section(section) RawConfigParser.set(self, section, option, val) self._modified = True def has_section(self, section): """Checks if `section` is a known configuration section.""" return section.lower() in self._cfg def has_option(self, option): """Checks if the option (section.option) is a known configuration option. """ # pylint: disable=W0221 # @pylint: _L A Z Y_ try: self._get_section_option(option) return True except(BadOptionError, NoSectionError, NoOptionError): return False def sections(self): """Returns an iterator object for all configuration sections.""" return self._cfg.iterkeys() class LazyConfigOption(object): """A simple container class for configuration settings. `LazyConfigOption` instances are required by `LazyConfig` instances, and instances of classes derived from `LazyConfig`, like the `Config` class. """ __slots__ = ('__cls', '__default', '__getter', '__validate') def __init__(self, cls, default, getter, validate=None): """Creates a new `LazyConfigOption` instance. Arguments: `cls` : type The class/type of the option's value `default` Default value of the option. Use ``None`` if the option should not have a default value. `getter` : callable A method's name of `RawConfigParser` and derived classes, to get a option's value, e.g. `self.getint`. `validate` : NoneType or a callable None or any method, that takes one argument, in order to check the value, when `LazyConfig.set()` is called. """ self.__cls = cls if not default is None: # enforce the type of the default value self.__default = self.__cls(default) else: self.__default = default if not callable(getter): raise TypeError('getter has to be a callable, got a %r' % getter.__class__.__name__) self.__getter = getter if validate and not callable(validate): raise TypeError('validate has to be callable or None, got a %r' % validate.__class__.__name__) self.__validate = validate @property def cls(self): """The class of the option's value e.g. `str`, `unicode` or `bool`.""" return self.__cls @property def default(self): """The option's default value, may be `None`""" return self.__default @property def getter(self): """The getter method or function to get the option's value""" return self.__getter @property def validate(self): """A method or function to validate the value""" return self.__validate class Config(LazyConfig): """This class is for reading vmm's configuration file.""" def __init__(self, filename): """Creates a new Config instance Arguments: `filename` : str path to the configuration file """ LazyConfig.__init__(self) self._cfg_filename = filename self._cfg_file = None self._missing = {} LCO = LazyConfigOption bool_t = self.bool_new self._cfg = { 'account': { 'delete_directory': LCO(bool_t, False, self.getboolean), 'directory_mode': LCO(int, 448, self.getint), 'disk_usage': LCO(bool_t, False, self.getboolean), 'password_length': LCO(int, 8, self.getint), 'random_password': LCO(bool_t, False, self.getboolean), }, 'bin': { 'dovecotpw': LCO(str, '/usr/sbin/dovecotpw', self.get, exec_ok), 'du': LCO(str, '/usr/bin/du', self.get, exec_ok), 'postconf': LCO(str, '/usr/sbin/postconf', self.get, exec_ok), }, 'database': { 'host': LCO(str, 'localhost', self.get), 'module': LCO(str, 'psycopg2', self.get, check_db_module), 'name': LCO(str, 'mailsys', self.get), 'pass': LCO(str, None, self.get), 'port': LCO(int, 5432, self.getint), 'sslmode': LCO(str, 'prefer', self.get, check_db_ssl_mode), 'user': LCO(str, None, self.get), }, 'domain': { 'auto_postmaster': LCO(bool_t, True, self.getboolean), 'delete_directory': LCO(bool_t, False, self.getboolean), 'directory_mode': LCO(int, 504, self.getint), 'force_deletion': LCO(bool_t, False, self.getboolean), 'imap': LCO(bool_t, True, self.getboolean), 'pop3': LCO(bool_t, True, self.getboolean), 'sieve': LCO(bool_t, True, self.getboolean), 'smtp': LCO(bool_t, True, self.getboolean), 'quota_bytes': LCO(str, '0', self.get_in_bytes, check_size_value), 'quota_messages': LCO(int, 0, self.getint), 'transport': LCO(str, 'dovecot:', self.get), }, 'mailbox': { 'folders': LCO(str, 'Drafts:Sent:Templates:Trash', self.unicode), 'format': LCO(str, 'maildir', self.get, check_mailbox_format), 'root': LCO(str, 'Maildir', self.unicode), 'subscribe': LCO(bool_t, True, self.getboolean), }, 'misc': { 'base_directory': LCO(str, '/srv/mail', self.get, is_dir), 'crypt_blowfish_rounds': LCO(int, 5, self.getint), 'crypt_sha256_rounds': LCO(int, 5000, self.getint), 'crypt_sha512_rounds': LCO(int, 5000, self.getint), 'dovecot_version': LCO(str, None, self.hexversion, check_version_format), 'password_scheme': LCO(str, 'CRAM-MD5', self.get, verify_scheme), }, } def load(self): """Loads the configuration, read only. Raises a ConfigError if the configuration syntax is invalid. """ self._cfg_file = open(self._cfg_filename, 'r') try: self.readfp(self._cfg_file) except (MissingSectionHeaderError, ParsingError), err: raise ConfigError(str(err), CONF_ERROR) self._cfg_file.close() def check(self): """Performs a configuration check. Raises a ConfigError if settings w/o a default value are missed. Or some settings have a invalid value. """ def iter_dict(): for section, options in self._missing.iteritems(): errmsg.write(_(u'* Section: %s\n') % section) errmsg.writelines(u' %s\n' % option for option in options) self._missing.clear() errmsg = None self._chk_non_default() miss_vers = 'misc' in self._missing and \ 'dovecot_version' in self._missing['misc'] if self._missing: errmsg = StringIO() errmsg.write(_(u'Check of configuration file %s failed.\n') % self._cfg_filename) errmsg.write(_(u'Missing options, which have no default value.\n')) iter_dict() self._chk_possible_values(miss_vers) if self._missing: if not errmsg: errmsg = StringIO() errmsg.write(_(u'Check of configuration file %s failed.\n') % self._cfg_filename) errmsg.write(_(u'Invalid configuration values.\n')) else: errmsg.write('\n' + _(u'Invalid configuration values.\n')) iter_dict() if errmsg: raise ConfigError(errmsg.getvalue(), CONF_ERROR) def hexversion(self, section, option): """Converts the version number (e.g.: 1.2.3) from the *option*'s value to an int.""" return version_hex(self.get(section, option)) def get_in_bytes(self, section, option): """Converts the size value (e.g.: 1024k) from the *option*'s value to a long""" return size_in_bytes(self.get(section, option)) def unicode(self, section, option): """Returns the value of the `option` from `section`, converted to Unicode.""" return get_unicode(self.get(section, option)) def _chk_non_default(self): """Checks all section's options for settings w/o a default value. Missing items will be stored in _missing. """ for section in self._cfg.iterkeys(): missing = [] for option, value in self._cfg[section].iteritems(): if (value.default is None and not RawConfigParser.has_option(self, section, option)): missing.append(option) if missing: self._missing[section] = missing def _chk_possible_values(self, miss_vers): """Check settings for which the possible values are known.""" if not miss_vers: value = self.get('misc', 'dovecot_version') if not VERSION_RE.match(value): self._missing['misc'] = ['version: ' + _(u"Not a valid Dovecot version: '%s'") % value] # section database db_err = [] value = self.dget('database.module').lower() if value not in DB_MODULES: db_err.append('module: ' + _(u"Unsupported database module: '%s'") % value) if value == 'psycopg2': value = self.dget('database.sslmode') if value not in DB_SSL_MODES: db_err.append('sslmode: ' + _(u"Unknown pgsql SSL mode: '%s'") % value) if db_err: self._missing['database'] = db_err # section mailbox value = self.dget('mailbox.format') if not known_format(value): self._missing['mailbox'] = ['format: ' + _(u"Unsupported mailbox format: '%s'") % value] # section domain try: value = self.dget('domain.quota_bytes') except (ValueError, TypeError), err: self._missing['domain'] = [u'quota_bytes: ' + str(err)] def is_dir(path): """Check if the expanded path is a directory. When the expanded path is a directory the expanded path will be returned. Otherwise a ConfigValueError will be raised. """ path = expand_path(path) if lisdir(path): return path raise ConfigValueError(_(u"No such directory: %s") % get_unicode(path)) def check_db_module(module): """Check if the *module* is a supported pgsql module.""" if module.lower() in DB_MODULES: return module raise ConfigValueError(_(u"Unsupported database module: '%s'") % get_unicode(module)) def check_db_ssl_mode(ssl_mode): """Check if the *ssl_mode* is one of the SSL modes, known by pgsql.""" if ssl_mode in DB_SSL_MODES: return ssl_mode raise ConfigValueError(_(u"Unknown pgsql SSL mode: '%s'") % get_unicode(ssl_mode)) def check_mailbox_format(format): """ Check if the mailbox format *format* is supported. When the *format* is supported it will be returned, otherwise a `ConfigValueError` will be raised. """ format = format.lower() if known_format(format): return format raise ConfigValueError(_(u"Unsupported mailbox format: '%s'") % get_unicode(format)) def check_size_value(value): """Check if the size value *value* has the proper format, e.g.: 1024k. Returns the validated value string if it has the expected format. Otherwise a `ConfigValueError` will be raised.""" try: tmp = size_in_bytes(value) except (TypeError, ValueError), err: raise ConfigValueError(_(u"Not a valid size value: '%s'") % get_unicode(value)) return value def check_version_format(version_string): """Check if the *version_string* has the proper format, e.g.: '1.2.3'. Returns the validated version string if it has the expected format. Otherwise a `ConfigValueError` will be raised. """ if not VERSION_RE.match(version_string): raise ConfigValueError(_(u"Not a valid Dovecot version: '%s'") % get_unicode(version_string)) return version_string def verify_scheme(scheme): """Checks if the password scheme *scheme* can be accepted and returns the verified scheme. """ try: scheme, encoding = _verify_scheme(scheme) except VMMError, err: # 'cast' it raise ConfigValueError(err.msg) if not encoding: return scheme return '%s.%s' % (scheme, encoding) del _ vmm-0.6.1/VirtualMailManager/transport.py0000644000175000017500000000566412033032424016645 0ustar pvopvo# -*- coding: UTF-8 -*- # Copyright (c) 2008 - 2012, Pascal Volk # See COPYING for distribution information. """ VirtualMailManager.transport ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Virtual Mail Manager's Transport class to manage the transport for domains and accounts. """ from VirtualMailManager.pycompat import any _ = lambda msg: msg class Transport(object): """A wrapper class that provides access to the transport table""" __slots__ = ('_tid', '_transport', '_dbh') def __init__(self, dbh, tid=None, transport=None): """Creates a new Transport instance. Either tid or transport must be specified. When both arguments are given, tid will be used. Keyword arguments: dbh -- a pyPgSQL.PgSQL.connection tid -- the id of a transport (int/long) transport -- the value of the transport (str) """ self._dbh = dbh self._tid = 0 assert any((tid, transport)) if tid: assert not isinstance(tid, bool) and isinstance(tid, (int, long)) self._load_by_id(tid) else: assert isinstance(transport, basestring) self._transport = transport self._load_by_name() @property def tid(self): """The transport's unique ID.""" return self._tid @property def transport(self): """The transport's value, ex: 'dovecot:'""" return self._transport def __eq__(self, other): if isinstance(other, self.__class__): return self._tid == other._tid return NotImplemented def __ne__(self, other): if isinstance(other, self.__class__): return self._tid != other._tid return NotImplemented def __str__(self): return self._transport def _load_by_id(self, tid): """load a transport by its id from the database""" dbc = self._dbh.cursor() dbc.execute('SELECT transport FROM transport WHERE tid = %s', (tid,)) result = dbc.fetchone() dbc.close() if not result: raise ValueError('Unknown transport id specified: %r' % tid) self._transport = result[0] self._tid = tid def _load_by_name(self): """Load a transport by its transport name from the database.""" dbc = self._dbh.cursor() dbc.execute('SELECT tid FROM transport WHERE transport = %s', (self._transport,)) result = dbc.fetchone() dbc.close() if result: self._tid = result[0] else: self._save() def _save(self): """Save the new transport in the database.""" dbc = self._dbh.cursor() dbc.execute("SELECT nextval('transport_id')") self._tid = dbc.fetchone()[0] dbc.execute('INSERT INTO transport (tid, transport) VALUES (%s, %s)', (self._tid, self._transport)) self._dbh.commit() dbc.close() del _ vmm-0.6.1/VirtualMailManager/cli/0000755000175000017500000000000012033032424014773 5ustar pvopvovmm-0.6.1/VirtualMailManager/cli/subcommands.py0000644000175000017500000012532512033032424017670 0ustar pvopvo# -*- coding: UTF-8 -*- # Copyright (c) 2007 - 2012, Pascal Volk # See COPYING for distribution information. """ VirtualMailManager.cli.subcommands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ VirtualMailManager's cli subcommands. """ import locale import os from textwrap import TextWrapper from time import strftime, strptime from VirtualMailManager import ENCODING from VirtualMailManager.cli import get_winsize, prog, w_err, w_std from VirtualMailManager.cli.clihelp import help_msgs from VirtualMailManager.common import human_size, size_in_bytes, \ version_str, format_domain_default from VirtualMailManager.constants import __copyright__, __date__, \ __version__, ACCOUNT_EXISTS, ALIAS_EXISTS, ALIASDOMAIN_ISDOMAIN, \ DOMAIN_ALIAS_EXISTS, INVALID_ARGUMENT, EX_MISSING_ARGS, \ RELOCATED_EXISTS, TYPE_ACCOUNT, TYPE_ALIAS, TYPE_RELOCATED from VirtualMailManager.errors import VMMError from VirtualMailManager.password import list_schemes from VirtualMailManager.serviceset import SERVICES __all__ = ( 'Command', 'RunContext', 'cmd_map', 'usage', 'alias_add', 'alias_delete', 'alias_info', 'aliasdomain_add', 'aliasdomain_delete', 'aliasdomain_info', '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', 'domain_note', 'get_user', 'help_', 'list_domains', 'list_pwschemes', 'list_users', 'list_aliases', 'list_relocated', 'list_addresses', '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 txt_wrpr = TextWrapper(width=get_winsize()[1] - 1) cmd_map = {} class Command(object): """Container class for command information.""" __slots__ = ('name', 'alias', 'func', 'args', 'descr') FMT_HLP_USAGE = """ usage: %(prog)s %(name)s %(args)s %(prog)s %(alias)s %(args)s """ def __init__(self, name, alias, func, args, descr): """Create a new Command instance. Arguments: `name` : str the command name, e.g. ``addalias`` `alias` : str the command's short alias, e.g. ``aa`` `func` : callable the function to handle the command `args` : str argument placeholders, e.g. ``aliasaddress`` `descr` : str short description of the command """ self.name = name self.alias = alias self.func = func self.args = args self.descr = descr @property def usage(self): """the command's usage info.""" return u'%s %s %s' % (prog, self.name, self.args) def help_(self): """Print the Command's help message to stdout.""" old_ii = txt_wrpr.initial_indent old_si = txt_wrpr.subsequent_indent txt_wrpr.subsequent_indent = (len(self.name) + 2) * ' ' w_std(txt_wrpr.fill('%s: %s' % (self.name, self.descr))) info = Command.FMT_HLP_USAGE % dict(alias=self.alias, args=self.args, name=self.name, prog=prog) w_std(info) txt_wrpr.initial_indent = txt_wrpr.subsequent_indent = ' ' try: [w_std(txt_wrpr.fill(_(para)) + '\n') for para in help_msgs[self.name]] except KeyError: w_err(1, _(u"Subcommand '%s' is not yet documented." % self.name), 'see also: vmm(1)') class RunContext(object): """Contains all information necessary to run a subcommand.""" __slots__ = ('argc', 'args', 'cget', 'hdlr', 'scmd') plan_a_b = _(u'Plan A failed ... trying Plan B: %(subcommand)s %(object)s') def __init__(self, argv, handler, command): """Create a new RunContext""" self.argc = len(argv) self.args = [unicode(arg, ENCODING) for arg in argv] self.cget = handler.cfg_dget self.hdlr = handler self.scmd = command def alias_add(ctx): """create a new alias e-mail address""" if ctx.argc < 3: usage(EX_MISSING_ARGS, _(u'Missing alias address and destination.'), ctx.scmd) elif ctx.argc < 4: usage(EX_MISSING_ARGS, _(u'Missing destination address.'), ctx.scmd) ctx.hdlr.alias_add(ctx.args[2].lower(), *ctx.args[3:]) def alias_delete(ctx): """delete the specified alias e-mail address or one of its destinations""" if ctx.argc < 3: usage(EX_MISSING_ARGS, _(u'Missing alias address.'), ctx.scmd) elif ctx.argc < 4: ctx.hdlr.alias_delete(ctx.args[2].lower()) else: ctx.hdlr.alias_delete(ctx.args[2].lower(), ctx.args[3:]) def alias_info(ctx): """show the destination(s) of the specified alias""" if ctx.argc < 3: usage(EX_MISSING_ARGS, _(u'Missing alias address.'), ctx.scmd) address = ctx.args[2].lower() try: _print_aliase_info(address, ctx.hdlr.alias_info(address)) except VMMError, err: if err.code is ACCOUNT_EXISTS: w_err(0, ctx.plan_a_b % {'subcommand': u'userinfo', 'object': address}) ctx.scmd = ctx.args[1] = 'userinfo' user_info(ctx) elif err.code is RELOCATED_EXISTS: w_err(0, ctx.plan_a_b % {'subcommand': u'relocatedinfo', 'object': address}) ctx.scmd = ctx.args[1] = 'relocatedinfo' relocated_info(ctx) else: raise def aliasdomain_add(ctx): """create a new alias for an existing domain""" if ctx.argc < 3: usage(EX_MISSING_ARGS, _(u'Missing alias domain name and destination ' u'domain name.'), ctx.scmd) elif ctx.argc < 4: usage(EX_MISSING_ARGS, _(u'Missing destination domain name.'), ctx.scmd) ctx.hdlr.aliasdomain_add(ctx.args[2].lower(), ctx.args[3].lower()) def aliasdomain_delete(ctx): """delete the specified alias domain""" if ctx.argc < 3: usage(EX_MISSING_ARGS, _(u'Missing alias domain name.'), ctx.scmd) ctx.hdlr.aliasdomain_delete(ctx.args[2].lower()) def aliasdomain_info(ctx): """show the destination of the given alias domain""" if ctx.argc < 3: usage(EX_MISSING_ARGS, _(u'Missing alias domain name.'), ctx.scmd) try: _print_aliasdomain_info(ctx.hdlr.aliasdomain_info(ctx.args[2].lower())) except VMMError, err: if err.code is ALIASDOMAIN_ISDOMAIN: w_err(0, ctx.plan_a_b % {'subcommand': u'domaininfo', 'object': ctx.args[2].lower()}) ctx.scmd = ctx.args[1] = 'domaininfo' domain_info(ctx) else: raise def aliasdomain_switch(ctx): """assign the given alias domain to an other domain""" if ctx.argc < 3: usage(EX_MISSING_ARGS, _(u'Missing alias domain name and destination ' u'domain name.'), ctx.scmd) elif ctx.argc < 4: usage(EX_MISSING_ARGS, _(u'Missing destination domain name.'), ctx.scmd) ctx.hdlr.aliasdomain_switch(ctx.args[2].lower(), ctx.args[3].lower()) def catchall_add(ctx): """create a new catchall alias e-mail address""" if ctx.argc < 3: usage(EX_MISSING_ARGS, _(u'Missing domain and destination.'), ctx.scmd) elif ctx.argc < 4: usage(EX_MISSING_ARGS, _(u'Missing destination address.'), ctx.scmd) ctx.hdlr.catchall_add(ctx.args[2].lower(), *ctx.args[3:]) def catchall_delete(ctx): """delete the specified destination or all of the catchall destination""" if ctx.argc < 3: usage(EX_MISSING_ARGS, _(u'Missing domain name.'), ctx.scmd) elif ctx.argc < 4: ctx.hdlr.catchall_delete(ctx.args[2].lower()) else: ctx.hdlr.catchall_delete(ctx.args[2].lower(), ctx.args[3:]) def catchall_info(ctx): """show the catchall destination(s) of the specified domain""" if ctx.argc < 3: usage(EX_MISSING_ARGS, _(u'Missing domain name.'), ctx.scmd) address = ctx.args[2].lower() _print_catchall_info(address, ctx.hdlr.catchall_info(address)) def config_get(ctx): """show the actual value of the configuration option""" if ctx.argc < 3: usage(EX_MISSING_ARGS, _(u"Missing option name."), ctx.scmd) noop = lambda option: option opt_formater = { 'misc.dovecot_version': version_str, 'domain.quota_bytes': human_size, } option = ctx.args[2].lower() w_std('%s = %s' % (option, opt_formater.get(option, noop)(ctx.cget(option)))) def config_set(ctx): """set a new value for the configuration option""" if ctx.argc < 3: usage(EX_MISSING_ARGS, _(u'Missing option and new value.'), ctx.scmd) if ctx.argc < 4: usage(EX_MISSING_ARGS, _(u'Missing new configuration value.'), ctx.scmd) ctx.hdlr.cfg_set(ctx.args[2].lower(), ctx.args[3]) def configure(ctx): """start interactive configuration mode""" if ctx.argc < 3: ctx.hdlr.configure() else: ctx.hdlr.configure(ctx.args[2].lower()) def domain_add(ctx): """create a new domain""" if ctx.argc < 3: usage(EX_MISSING_ARGS, _(u'Missing domain name.'), ctx.scmd) elif ctx.argc < 4: ctx.hdlr.domain_add(ctx.args[2].lower()) else: ctx.hdlr.domain_add(ctx.args[2].lower(), ctx.args[3]) if ctx.cget('domain.auto_postmaster'): w_std(_(u'Creating account for postmaster@%s') % ctx.args[2].lower()) ctx.scmd = 'useradd' ctx.args = [prog, ctx.scmd, u'postmaster@' + ctx.args[2].lower()] ctx.argc = 3 user_add(ctx) def domain_delete(ctx): """delete the given domain and all its alias domains""" if ctx.argc < 3: usage(EX_MISSING_ARGS, _(u'Missing domain name.'), ctx.scmd) elif ctx.argc < 4: ctx.hdlr.domain_delete(ctx.args[2].lower()) elif ctx.args[3].lower() == 'force': ctx.hdlr.domain_delete(ctx.args[2].lower(), True) else: usage(INVALID_ARGUMENT, _(u"Invalid argument: '%s'") % ctx.args[3], ctx.scmd) def domain_info(ctx): """display information about the given domain""" if ctx.argc < 3: usage(EX_MISSING_ARGS, _(u'Missing domain name.'), ctx.scmd) if ctx.argc < 4: details = None else: details = ctx.args[3].lower() if details not in ('accounts', 'aliasdomains', 'aliases', 'full', 'relocated', 'catchall'): usage(INVALID_ARGUMENT, _(u"Invalid argument: '%s'") % details, ctx.scmd) try: info = ctx.hdlr.domain_info(ctx.args[2].lower(), details) except VMMError, err: if err.code is DOMAIN_ALIAS_EXISTS: w_err(0, ctx.plan_a_b % {'subcommand': u'aliasdomaininfo', 'object': ctx.args[2].lower()}) ctx.scmd = ctx.args[1] = 'aliasdomaininfo' aliasdomain_info(ctx) else: raise else: q_limit = u'Storage: %(bytes)s; Messages: %(messages)s' if not details: info['bytes'] = human_size(info['bytes']) info['messages'] = locale.format('%d', info['messages'], True).decode(ENCODING, 'replace') info['quota limit/user'] = q_limit % info _print_info(ctx, info, _(u'Domain')) else: info[0]['bytes'] = human_size(info[0]['bytes']) info[0]['messages'] = locale.format('%d', info[0]['messages'], True).decode(ENCODING, 'replace') info[0]['quota limit/user'] = q_limit % info[0] _print_info(ctx, info[0], _(u'Domain')) if details == u'accounts': _print_list(info[1], _(u'accounts')) elif details == u'aliasdomains': _print_list(info[1], _(u'alias domains')) elif details == u'aliases': _print_list(info[1], _(u'aliases')) elif details == u'relocated': _print_list(info[1], _(u'relocated users')) elif details == u'catchall': _print_list(info[1], _(u'catch-all destinations')) else: _print_list(info[1], _(u'alias domains')) _print_list(info[2], _(u'accounts')) _print_list(info[3], _(u'aliases')) _print_list(info[4], _(u'relocated users')) _print_list(info[5], _(u'catch-all destinations')) def domain_quota(ctx): """update the quota limit of the specified domain""" if ctx.argc < 3: usage(EX_MISSING_ARGS, _(u'Missing domain name and storage value.'), ctx.scmd) if ctx.argc < 4: usage(EX_MISSING_ARGS, _(u'Missing storage value.'), ctx.scmd) messages = 0 force = None try: bytes_ = size_in_bytes(ctx.args[3]) except (ValueError, TypeError): usage(INVALID_ARGUMENT, _(u"Invalid storage value: '%s'") % ctx.args[3], ctx.scmd) if ctx.argc < 5: pass elif ctx.argc < 6: try: messages = int(ctx.args[4]) except ValueError: if ctx.args[4].lower() != 'force': usage(INVALID_ARGUMENT, _(u"Neither a valid number of messages nor the keyword " u"'force': '%s'") % ctx.args[4], ctx.scmd) force = 'force' else: try: messages = int(ctx.args[4]) except ValueError: usage(INVALID_ARGUMENT, _(u"Not a valid number of messages: '%s'") % ctx.args[4], ctx.scmd) if ctx.args[5].lower() != 'force': usage(INVALID_ARGUMENT, _(u"Invalid argument: '%s'") % ctx.args[5], ctx.scmd) force = 'force' ctx.hdlr.domain_quotalimit(ctx.args[2].lower(), bytes_, messages, force) def domain_services(ctx): """allow all named service and block the uncredited.""" if ctx.argc < 3: usage(EX_MISSING_ARGS, _(u'Missing domain name.'), ctx.scmd) services = [] force = False if ctx.argc is 3: pass elif ctx.argc is 4: arg = ctx.args[3].lower() if arg in SERVICES: services.append(arg) elif arg == 'force': force = True else: usage(INVALID_ARGUMENT, _(u"Invalid argument: '%s'") % arg, ctx.scmd) else: services.extend([service.lower() for service in ctx.args[3:-1]]) arg = ctx.args[-1].lower() if arg == 'force': force = True else: services.append(arg) unknown = [service for service in services if service not in SERVICES] if unknown: usage(INVALID_ARGUMENT, _(u'Invalid service arguments: %s') % ' '.join(unknown), ctx.scmd) ctx.hdlr.domain_services(ctx.args[2].lower(), (None, 'force')[force], *services) def domain_transport(ctx): """update the transport of the specified domain""" if ctx.argc < 3: usage(EX_MISSING_ARGS, _(u'Missing domain name and new transport.'), ctx.scmd) if ctx.argc < 4: usage(EX_MISSING_ARGS, _(u'Missing new transport.'), ctx.scmd) if ctx.argc < 5: ctx.hdlr.domain_transport(ctx.args[2].lower(), ctx.args[3]) else: force = ctx.args[4].lower() if force != 'force': usage(INVALID_ARGUMENT, _(u"Invalid argument: '%s'") % force, ctx.scmd) 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: usage(EX_MISSING_ARGS, _(u'Missing UID.'), ctx.scmd) _print_info(ctx, ctx.hdlr.user_by_uid(ctx.args[2]), _(u'Account')) def help_(ctx): """print help messages.""" if ctx.argc > 2: hlptpc = ctx.args[2].lower() if hlptpc in cmd_map: topic = hlptpc else: for scmd in cmd_map.itervalues(): if scmd.alias == hlptpc: topic = scmd.name break else: usage(INVALID_ARGUMENT, _(u"Unknown help topic: '%s'") % ctx.args[2], ctx.scmd) if topic != u'help': return cmd_map[topic].help_() old_ii = txt_wrpr.initial_indent old_si = txt_wrpr.subsequent_indent txt_wrpr.initial_indent = ' ' # len(max(_overview.iterkeys(), key=len)) #Py25 txt_wrpr.subsequent_indent = 20 * ' ' order = cmd_map.keys() order.sort() w_std(_(u'List of available subcommands:') + '\n') for key in order: w_std('\n'.join(txt_wrpr.wrap('%-18s %s' % (key, cmd_map[key].descr)))) txt_wrpr.initial_indent = old_ii txt_wrpr.subsequent_indent = old_si txt_wrpr.initial_indent = '' def list_domains(ctx): """list all domains / search domains by pattern""" matching = ctx.argc > 2 if matching: gids, domains = ctx.hdlr.domain_list(ctx.args[2].lower()) else: gids, domains = ctx.hdlr.domain_list() _print_domain_list(gids, domains, matching) def list_pwschemes(ctx_unused): """Prints all usable password schemes and password encoding suffixes.""" # TODO: Remove trailing colons from keys. # For now it is to late, the translators has stared their work keys = (_(u'Usable password schemes:'), _(u'Usable encoding suffixes:')) old_ii, old_si = txt_wrpr.initial_indent, txt_wrpr.subsequent_indent txt_wrpr.initial_indent = txt_wrpr.subsequent_indent = '\t' txt_wrpr.width = txt_wrpr.width - 8 for key, value in zip(keys, list_schemes()): if key.endswith(':'): # who knows … (see TODO above) #key = key.rpartition(':')[0] key = key[:-1] # This one is for Py24 w_std(key, len(key) * '-') w_std('\n'.join(txt_wrpr.wrap(' '.join(value))), '') txt_wrpr.initial_indent, txt_wrpr.subsequent_indent = old_ii, old_si txt_wrpr.width = txt_wrpr.width + 8 def list_addresses(ctx, limit=None): """List all addresses / search addresses by pattern. The output can be limited with TYPE_ACCOUNT, TYPE_ALIAS and TYPE_RELOCATED, which can be bitwise ORed as a combination. Not specifying a limit is the same as combining all three.""" if limit is None: limit = TYPE_ACCOUNT | TYPE_ALIAS | TYPE_RELOCATED matching = ctx.argc > 2 if matching: gids, addresses = ctx.hdlr.address_list(limit, ctx.args[2].lower()) else: gids, addresses = ctx.hdlr.address_list(limit) _print_address_list(limit, gids, addresses, matching) def list_users(ctx): """list all user accounts / search user accounts by pattern""" return list_addresses(ctx, TYPE_ACCOUNT) def list_aliases(ctx): """list all aliases / search aliases by pattern""" return list_addresses(ctx, TYPE_ALIAS) def list_relocated(ctx): """list all relocated records / search relocated records by pattern""" return list_addresses(ctx, TYPE_RELOCATED) def relocated_add(ctx): """create a new record for a relocated user""" if ctx.argc < 3: usage(EX_MISSING_ARGS, _(u'Missing relocated address and destination.'), ctx.scmd) elif ctx.argc < 4: usage(EX_MISSING_ARGS, _(u'Missing destination address.'), ctx.scmd) ctx.hdlr.relocated_add(ctx.args[2].lower(), ctx.args[3]) def relocated_delete(ctx): """delete the record of the relocated user""" if ctx.argc < 3: usage(EX_MISSING_ARGS, _(u'Missing relocated address.'), ctx.scmd) ctx.hdlr.relocated_delete(ctx.args[2].lower()) def relocated_info(ctx): """print information about a relocated user""" if ctx.argc < 3: usage(EX_MISSING_ARGS, _(u'Missing relocated address.'), ctx.scmd) relocated = ctx.args[2].lower() try: _print_relocated_info(addr=relocated, dest=ctx.hdlr.relocated_info(relocated)) except VMMError, err: if err.code is ACCOUNT_EXISTS: w_err(0, ctx.plan_a_b % {'subcommand': u'userinfo', 'object': relocated}) ctx.scmd = ctx.args[1] = 'userinfoi' user_info(ctx) elif err.code is ALIAS_EXISTS: w_err(0, ctx.plan_a_b % {'subcommand': u'aliasinfo', 'object': relocated}) ctx.scmd = ctx.args[1] = 'aliasinfo' alias_info(ctx) else: raise def user_add(ctx): """create a new e-mail user with the given address""" if ctx.argc < 3: usage(EX_MISSING_ARGS, _(u'Missing e-mail address.'), ctx.scmd) elif ctx.argc < 4: password = None else: password = ctx.args[3] gen_pass = ctx.hdlr.user_add(ctx.args[2].lower(), password) if ctx.argc < 4 and gen_pass: w_std(_(u"Generated password: %s") % gen_pass) def user_delete(ctx): """delete the specified user""" if ctx.argc < 3: usage(EX_MISSING_ARGS, _(u'Missing e-mail address.'), ctx.scmd) elif ctx.argc < 4: ctx.hdlr.user_delete(ctx.args[2].lower()) elif ctx.args[3].lower() == 'force': ctx.hdlr.user_delete(ctx.args[2].lower(), True) else: usage(INVALID_ARGUMENT, _(u"Invalid argument: '%s'") % ctx.args[3], ctx.scmd) def user_info(ctx): """display information about the given address""" if ctx.argc < 3: usage(EX_MISSING_ARGS, _(u'Missing e-mail address.'), ctx.scmd) if ctx.argc < 4: details = None else: details = ctx.args[3].lower() if details not in ('aliases', 'du', 'full'): usage(INVALID_ARGUMENT, _(u"Invalid argument: '%s'") % details, ctx.scmd) try: info = ctx.hdlr.user_info(ctx.args[2].lower(), details) except VMMError, err: if err.code is ALIAS_EXISTS: w_err(0, ctx.plan_a_b % {'subcommand': u'aliasinfo', 'object': ctx.args[2].lower()}) ctx.scmd = ctx.args[1] = 'aliasinfo' alias_info(ctx) elif err.code is RELOCATED_EXISTS: w_err(0, ctx.plan_a_b % {'subcommand': u'relocatedinfo', 'object': ctx.args[2].lower()}) ctx.scmd = ctx.args[1] = 'relocatedinfo' relocated_info(ctx) else: raise else: if details in (None, 'du'): info['quota storage'] = _format_quota_usage(info['ql_bytes'], info['uq_bytes'], True, info['ql_domaindefault']) info['quota messages'] = \ _format_quota_usage(info['ql_messages'], info['uq_messages'], domaindefault=info['ql_domaindefault']) _print_info(ctx, info, _(u'Account')) else: info[0]['quota storage'] = _format_quota_usage(info[0]['ql_bytes'], info[0]['uq_bytes'], True, info[0]['ql_domaindefault']) info[0]['quota messages'] = \ _format_quota_usage(info[0]['ql_messages'], info[0]['uq_messages'], domaindefault=info[0]['ql_domaindefault']) _print_info(ctx, info[0], _(u'Account')) _print_list(info[1], _(u'alias addresses')) def user_name(ctx): """set or update the real name for an address""" if ctx.argc < 3: usage(EX_MISSING_ARGS, _(u"Missing e-mail address and user's name."), ctx.scmd) elif ctx.argc < 4: name = None else: name = ctx.args[3] ctx.hdlr.user_name(ctx.args[2].lower(), name) def user_password(ctx): """update the password for the given address""" if ctx.argc < 3: usage(EX_MISSING_ARGS, _(u'Missing e-mail address.'), ctx.scmd) elif ctx.argc < 4: password = None else: password = ctx.args[3] 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: usage(EX_MISSING_ARGS, _(u'Missing e-mail address and storage value.'), ctx.scmd) elif ctx.argc < 4: usage(EX_MISSING_ARGS, _(u'Missing storage value.'), ctx.scmd) if ctx.args[3] != 'domain': try: bytes_ = size_in_bytes(ctx.args[3]) except (ValueError, TypeError): usage(INVALID_ARGUMENT, _(u"Invalid storage value: '%s'") % ctx.args[3], ctx.scmd) else: bytes_ = ctx.args[3] if ctx.argc < 5: messages = 0 else: try: messages = int(ctx.args[4]) except ValueError: usage(INVALID_ARGUMENT, _(u"Not a valid number of messages: '%s'") % ctx.args[4], ctx.scmd) ctx.hdlr.user_quotalimit(ctx.args[2].lower(), bytes_, messages) def user_services(ctx): """allow all named service and block the uncredited.""" if ctx.argc < 3: usage(EX_MISSING_ARGS, _(u'Missing e-mail address.'), ctx.scmd) services = [] if ctx.argc >= 4: services.extend([service.lower() for service in ctx.args[3:]]) unknown = [service for service in services if service not in SERVICES] if unknown and ctx.args[3] != 'domain': usage(INVALID_ARGUMENT, _(u'Invalid service arguments: %s') % ' '.join(unknown), ctx.scmd) ctx.hdlr.user_services(ctx.args[2].lower(), *services) def user_transport(ctx): """update the transport of the given address""" if ctx.argc < 3: usage(EX_MISSING_ARGS, _(u'Missing e-mail address and transport.'), ctx.scmd) if ctx.argc < 4: usage(EX_MISSING_ARGS, _(u'Missing transport.'), ctx.scmd) ctx.hdlr.user_transport(ctx.args[2].lower(), ctx.args[3]) def usage(errno, errmsg, subcommand=None): """print usage message for the given command or all commands. When errno > 0, sys,exit(errno) will interrupt the program. """ if subcommand and subcommand in cmd_map: w_err(errno, _(u"Error: %s") % errmsg, _(u"usage: ") + cmd_map[subcommand].usage) # TP: Please adjust translated words like the original text. # (It's a table header.) Extract from usage text: # usage: vmm subcommand arguments # short long # subcommand arguments # # da domainadd fqdn [transport] # dd domaindelete fqdn [force] u_head = _(u"""usage: %s subcommand arguments short long subcommand arguments\n""") % prog order = cmd_map.keys() order.sort() w_err(0, u_head) for key in order: scmd = cmd_map[key] w_err(0, ' %-5s %-19s %s' % (scmd.alias, scmd.name, scmd.args)) w_err(errno, '', _(u"Error: %s") % errmsg) def version(ctx_unused): """Write version and copyright information to stdout.""" w_std('%s, %s %s (%s %s)\nPython %s %s %s\n\n%s\n%s %s' % (prog, # TP: The words 'from', 'version' and 'on' are used in # the version information, e.g.: # vmm, version 0.5.2 (from 09/09/09) # Python 2.5.4 on FreeBSD _(u'version'), __version__, _(u'from'), strftime(locale.nl_langinfo(locale.D_FMT), strptime(__date__, '%Y-%m-%d')).decode(ENCODING, 'replace'), os.sys.version.split()[0], _(u'on'), os.uname()[0], __copyright__, prog, _(u'is free software and comes with ABSOLUTELY NO WARRANTY.'))) def update_cmd_map(): """Update the cmd_map, after gettext's _ was installed.""" cmd = Command cmd_map.update({ # Account commands 'getuser': cmd('getuser', 'gu', get_user, 'uid', _(u'get the address of the user with the given UID')), 'useradd': cmd('useradd', 'ua', user_add, 'address [password]', _(u'create a new e-mail user with the given address')), 'userdelete': cmd('userdelete', 'ud', user_delete, 'address [force]', _(u'delete the specified user')), 'userinfo': cmd('userinfo', 'ui', user_info, 'address [details]', _(u'display information about the given address')), 'username': cmd('username', 'un', user_name, 'address [name]', _(u'set, update or delete the real name for an address')), 'userpassword': cmd('userpassword', 'up', user_password, 'address [password]', _(u'update the password for the given address')), 'userquota': cmd('userquota', 'uq', user_quota, 'address storage [messages] | address domain', _(u'update the quota limit for the given address')), 'userservices': cmd('userservices', 'us', user_services, 'address [service ...] | address domain', _(u'enables the specified services and disables all ' u'not specified services')), 'usertransport': cmd('usertransport', 'ut', user_transport, 'address transport | address domain', _(u'update the transport of the given address')), 'usernote': cmd('usernote', 'uo', user_note, 'address [note]', _(u'set, update or delete 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 ' u'destinations')), 'aliasdelete': cmd('aliasdelete', 'ad', alias_delete, 'address [destination ...]', _(u'delete the specified alias e-mail address or one ' u'of its destinations')), 'aliasinfo': cmd('aliasinfo', 'ai', alias_info, 'address', _(u'show the destination(s) of the specified alias')), # AliasDomain commands 'aliasdomainadd': cmd('aliasdomainadd', 'ada', aliasdomain_add, 'fqdn destination', _(u'create a new alias for an existing domain')), 'aliasdomaindelete': cmd('aliasdomaindelete', 'add', aliasdomain_delete, 'fqdn', _(u'delete the specified alias domain')), 'aliasdomaininfo': cmd('aliasdomaininfo', 'adi', aliasdomain_info, 'fqdn', _(u'show the destination of the given alias domain')), 'aliasdomainswitch': cmd('aliasdomainswitch', 'ads', aliasdomain_switch, 'fqdn destination', _(u'assign the given alias ' 'domain to an other domain')), # CatchallAlias commands 'catchalladd': cmd('catchalladd', 'caa', catchall_add, 'fqdn destination ...', _(u'add one or more catch-all destinations for a ' u'domain')), 'catchalldelete': cmd('catchalldelete', 'cad', catchall_delete, 'fqdn [destination ...]', _(u'delete the specified catch-all destination or all ' u'of a domain\'s destinations')), 'catchallinfo': cmd('catchallinfo', 'cai', catchall_info, 'fqdn', _(u'show the catch-all destination(s) of the ' u'specified domain')), # Domain commands 'domainadd': cmd('domainadd', 'da', domain_add, 'fqdn [transport]', _(u'create a new domain')), 'domaindelete': cmd('domaindelete', 'dd', domain_delete, 'fqdn [force]', _(u'delete the given domain and all its alias domains')), 'domaininfo': cmd('domaininfo', 'di', domain_info, 'fqdn [details]', _(u'display information about the given domain')), 'domainquota': cmd('domainquota', 'dq', domain_quota, 'fqdn storage [messages] [force]', _(u'update the quota limit of the specified domain')), 'domainservices': cmd('domainservices', 'ds', domain_services, 'fqdn [service ...] [force]', _(u'enables the specified services and disables all ' u'not specified services of the given domain')), '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'set, update or delete the note of the given domain')), # List commands 'listdomains': cmd('listdomains', 'ld', list_domains, '[pattern]', _(u'list all domains or search for domains by pattern')), 'listaddresses': cmd('listaddresses', 'll', list_addresses, '[pattern]', _(u'list all addresses or search for addresses by ' u'pattern')), 'listusers': cmd('listusers', 'lu', list_users, '[pattern]', _(u'list all user accounts or search for accounts by ' u'pattern')), 'listaliases': cmd('listaliases', 'la', list_aliases, '[pattern]', _(u'list all aliases or search for aliases by pattern')), 'listrelocated': cmd('listrelocated', 'lr', list_relocated, '[pattern]', _(u'list all relocated users or search for relocated ' u'users by pattern')), # Relocated commands 'relocatedadd': cmd('relocatedadd', 'ra', relocated_add, 'address newaddress', _(u'create a new record for a relocated user')), 'relocateddelete': cmd('relocateddelete', 'rd', relocated_delete, 'address', _(u'delete the record of the relocated user')), 'relocatedinfo': cmd('relocatedinfo', 'ri', relocated_info, 'address', _(u'print information about a relocated user')), # cli commands 'configget': cmd('configget', 'cg', config_get, 'option', _('show the actual value of the configuration option')), 'configset': cmd('configset', 'cs', config_set, 'option value', _('set a new value for the configuration option')), 'configure': cmd('configure', 'cf', configure, '[section]', _(u'start interactive configuration mode')), 'listpwschemes': cmd('listpwschemes', 'lp', list_pwschemes, '', _(u'lists all usable password schemes and password ' u'encoding suffixes')), 'help': cmd('help', 'h', help_, '[subcommand]', _(u'show a help overview or help for the given subcommand')), 'version': cmd('version', 'v', version, '', _(u'show version and copyright information')), }) def _get_order(ctx): """returns a tuple with (key, 1||0) tuples. Used by functions, which get a dict from the handler.""" order = () if ctx.scmd == 'domaininfo': order = ((u'domain name', 0), (u'gid', 1), (u'domain directory', 0), (u'quota limit/user', 0), (u'active services', 0), (u'transport', 0), (u'alias domains', 0), (u'accounts', 0), (u'aliases', 0), (u'relocated', 0), (u'catch-all dests', 0)) elif ctx.scmd == 'userinfo': if ctx.argc == 4 and ctx.args[3] != u'aliases' or \ ctx.cget('account.disk_usage'): order = ((u'address', 0), (u'name', 0), (u'uid', 1), (u'gid', 1), (u'home', 0), (u'mail_location', 0), (u'quota storage', 0), (u'quota messages', 0), (u'disk usage', 0), (u'transport', 0), (u'smtp', 1), (u'pop3', 1), (u'imap', 1), (u'sieve', 1)) else: order = ((u'address', 0), (u'name', 0), (u'uid', 1), (u'gid', 1), (u'home', 0), (u'mail_location', 0), (u'quota storage', 0), (u'quota messages', 0), (u'transport', 0), (u'smtp', 1), (u'pop3', 1), (u'imap', 1), (u'sieve', 1)) elif ctx.scmd == 'getuser': order = ((u'uid', 1), (u'gid', 1), (u'address', 0)) return order def _format_quota_usage(limit, used, human=False, domaindefault=False): """Put quota's limit / usage / percentage in a formatted string.""" if human: q_usage = { 'used': human_size(used), 'limit': human_size(limit), } else: q_usage = { 'used': locale.format('%d', used, True).decode(ENCODING, 'replace'), 'limit': locale.format('%d', limit, True).decode(ENCODING, 'replace'), } if limit: q_usage['percent'] = locale.format('%6.2f', 100. / limit * used, True) else: q_usage['percent'] = locale.format('%6.2f', 0, True) # Py25: fmt = format_domain_default if domaindefault else lambda s: s if domaindefault: fmt = format_domain_default else: fmt = lambda s: s # TP: e.g.: [ 0.00%] 21.09 KiB/1.00 GiB return fmt(_(u'[%(percent)s%%] %(used)s/%(limit)s') % q_usage) def _print_info(ctx, info, title): """Print info dicts.""" # TP: used in e.g. 'Domain information' or 'Account information' msg = u'%s %s' % (title, _(u'information')) w_std(msg, u'-' * len(msg)) for key, upper in _get_order(ctx): if upper: w_std(u'\t%s: %s' % (key.upper().ljust(17, u'.'), info[key])) else: w_std(u'\t%s: %s' % (key.title().ljust(17, u'.'), info[key])) print note = info.get('note') if note: _print_note(note + '\n') def _print_note(note): msg = _(u'Note') w_std(msg, u'-' * len(msg)) old_ii = txt_wrpr.initial_indent old_si = txt_wrpr.subsequent_indent txt_wrpr.initial_indent = txt_wrpr.subsequent_indent = '\t' txt_wrpr.width -= 8 for para in note.split('\n'): w_std(txt_wrpr.fill(para)) txt_wrpr.width += 8 txt_wrpr.subsequent_indent = old_si txt_wrpr.initial_indent = old_ii def _print_list(alist, title): """Print a list.""" # TP: used in e.g. 'Existing alias addresses' or 'Existing accounts' msg = u'%s %s' % (_(u'Existing'), title) w_std(msg, u'-' * len(msg)) if alist: if title != _(u'alias domains'): w_std(*(u'\t%s' % item for item in alist)) else: for domain in alist: if not domain.startswith('xn--'): w_std(u'\t%s' % domain) else: w_std(u'\t%s (%s)' % (domain, domain.decode('idna'))) print else: w_std(_(u'\tNone'), '') def _print_aliase_info(alias, destinations): """Print the alias address and all its destinations""" title = _(u'Alias information') w_std(title, u'-' * len(title)) w_std(_(u'\tMail for %s will be redirected to:') % alias) w_std(*(u'\t * %s' % dest for dest in destinations)) print def _print_catchall_info(domain, destinations): """Print the catchall destinations of a domain""" title = _(u'Catch-all information') w_std(title, u'-' * len(title)) w_std(_(u'\tMail to unknown local-parts in domain %s will be sent to:') % domain) w_std(*(u'\t * %s' % dest for dest in destinations)) print def _print_relocated_info(**kwargs): """Print the old and new addresses of a relocated user.""" title = _(u'Relocated information') w_std(title, u'-' * len(title)) w_std(_(u"\tUser '%(addr)s' has moved to '%(dest)s'") % kwargs, '') def _format_domain(domain, main=True): """format (prefix/convert) the domain name.""" if domain.startswith('xn--'): domain = u'%s (%s)' % (domain, domain.decode('idna')) if main: return u'\t[+] %s' % domain return u'\t[-] %s' % domain def _print_domain_list(dids, domains, matching): """Print a list of (matching) domains/alias domains.""" if matching: title = _(u'Matching domains') else: title = _(u'Existing domains') w_std(title, '-' * len(title)) if domains: for did in dids: if domains[did][0] is not None: w_std(_format_domain(domains[did][0])) if len(domains[did]) > 1: w_std(*(_format_domain(a, False) for a in domains[did][1:])) else: w_std(_('\tNone')) print def _print_address_list(which, dids, addresses, matching): """Print a list of (matching) addresses.""" _trans = { TYPE_ACCOUNT: _('user accounts'), TYPE_ALIAS: _('aliases'), TYPE_RELOCATED: _('relocated users'), TYPE_ACCOUNT | TYPE_ALIAS: _('user accounts and aliases'), TYPE_ACCOUNT | TYPE_RELOCATED: _('user accounts and relocated users'), TYPE_ALIAS | TYPE_RELOCATED: _('aliases and relocated users'), TYPE_ACCOUNT | TYPE_ALIAS | TYPE_RELOCATED: _('addresses'), } try: if matching: title = _(u'Matching %s') % _trans[which] else: title = _(u'Existing %s') % _trans[which] w_std(title, '-' * len(title)) except KeyError: raise VMMError(_("Invalid address type for list: '%s'") % which, INVALID_ARGUMENT) if addresses: if which & (which - 1) == 0: # only one type is requested, so no type indicator _trans = {TYPE_ACCOUNT: '', TYPE_ALIAS: '', TYPE_RELOCATED: ''} else: # TP: the letters 'u', 'a' and 'r' are abbreviations of user, # alias and relocated user _trans = { TYPE_ACCOUNT: _('u'), TYPE_ALIAS: _('a'), TYPE_RELOCATED: _('r'), } for did in dids: for addr, atype, aliasdomain in addresses[did]: if aliasdomain: leader = '[%s-]' % _trans[atype] else: leader = '[%s+]' % _trans[atype] w_std('\t%s %s' % (leader, addr)) else: w_std(_('\tNone')) print def _print_aliasdomain_info(info): """Print alias domain information.""" title = _(u'Alias domain information') for key in ('alias', 'domain'): if info[key].startswith('xn--'): info[key] = u'%s (%s)' % (info[key], info[key].decode('idna')) w_std(title, '-' * len(title), _('\tThe alias domain %(alias)s belongs to:\n\t * %(domain)s') % info, '') del _ vmm-0.6.1/VirtualMailManager/cli/handler.py0000644000175000017500000000670112033032424016766 0ustar pvopvo# -*- coding: UTF-8 -*- # Copyright (c) 2010 - 2012, Pascal Volk # See COPYING for distribution information. """ VirtualMailManager.cli.handler ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ A derived Handler class with a few changes/additions for cli use. """ import os from VirtualMailManager.errors import VMMError from VirtualMailManager.handler import Handler from VirtualMailManager.cli import read_pass from VirtualMailManager.cli.config import CliConfig as Cfg from VirtualMailManager.constants import ACCOUNT_EXISTS, INVALID_SECTION, \ NO_SUCH_ACCOUNT, TYPE_ACCOUNT from VirtualMailManager.password import randompw _ = lambda msg: msg class CliHandler(Handler): """This class uses a `CliConfig` for configuration stuff, instead of the non-interactive `Config` class. It provides the additional methods cfgSet() and configure(). Additionally it uses `VirtualMailManager.cli.read_pass()` for for the interactive password dialog. """ __slots__ = () # nothing additional, also no __dict__/__weakref__ def __init__(self): """Creates a new CliHandler instance. Throws a NotRootError if your uid is greater 0. """ # Overwrite the parent CTor partly, we use the CliConfig class # and add some command line checks. skip_some_checks = os.sys.argv[1] in ('cf', 'configure', 'h', 'help', 'v', 'version') super(CliHandler, self).__init__(skip_some_checks) self._cfg = Cfg(self._cfg_fname) self._cfg.load() def cfg_set(self, option, value): """Set a new value for the given option.""" return self._cfg.set(option, value) def configure(self, section=None): """Starts the interactive configuration. Configures in interactive mode options in the given ``section``. If no section is given (default) all options from all sections will be prompted. """ if section is None: self._cfg.configure(self._cfg.sections()) elif self._cfg.has_section(section): self._cfg.configure([section]) else: raise VMMError(_(u"Invalid section: '%s'") % section, INVALID_SECTION) def user_add(self, emailaddress, password=None): """Override the parent user_add() - add the interactive password dialog. Returns the generated password, if account.random_password == True. """ acc = self._get_account(emailaddress) if acc: raise VMMError(_(u"The account '%s' already exists.") % acc.address, ACCOUNT_EXISTS) self._is_other_address(acc.address, TYPE_ACCOUNT) rand_pass = self._cfg.dget('account.random_password') if password is None: password = (read_pass, randompw)[rand_pass]() acc.set_password(password) acc.save() self._make_account_dirs(acc) return (None, password)[rand_pass] def user_password(self, emailaddress, password=None): """Override the parent user_password() - add the interactive password dialog.""" acc = self._get_account(emailaddress) if not acc: raise VMMError(_(u"The account '%s' does not exist.") % acc.address, NO_SUCH_ACCOUNT) if not isinstance(password, basestring) or not password: password = read_pass() acc.modify('password', password) del _ vmm-0.6.1/VirtualMailManager/cli/clihelp.py0000644000175000017500000003424312033032424016773 0ustar pvopvo# -*- coding: UTF-8 -*- # Copyright (c) 2012, Pascal Volk # See COPYING for distribution information. """ VirtualMailManager.cli.vmmhelp ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Virtual Mail Manager's command line help. """ _ = lambda msg: msg help_msgs = { # TP: There are some words enclosed within angle brackets '<'word'>'. They # are used to indicate replaceable arguments. Please do not translate them. # # The descriptions of subcommands may contain the both keywords 'domain' # and 'force', enclosed within single quotes. Please keep them as they are. # # TP: description of subcommand configget 'configget': (_(u"""This subcommand is used to display the actual value of the given configuration