From a68524264deb6223363194af0667c9b718ad28d4 Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Wed, 25 Sep 2013 15:02:24 +0200 Subject: [PATCH 1/6] Two fixes Don't send Content-Length header if not needed. (see #480) Always use build_header... (see #473) --- modoboa/extensions/webmail/views.py | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/modoboa/extensions/webmail/views.py b/modoboa/extensions/webmail/views.py index 5d536f5aa..30c3055c6 100644 --- a/modoboa/extensions/webmail/views.py +++ b/modoboa/extensions/webmail/views.py @@ -1,6 +1,6 @@ # coding: utf-8 import os -from rfc6266 import build_header +from rfc6266 import build_header, parse_headers from django.conf import settings from django.http import HttpResponse from django.template import Template, Context @@ -29,6 +29,14 @@ from templatetags import webextras def getattachment(request): """Fetch a message attachment + The tricky part here consists in properly format the + *Content-Disposition* header. We use the rfc6266 package to encode + headers according to RFC5987 and RFC6266. + + Even if the header received from Dovecot is already encoded, we + decode and re-encode it to make sure it uses UTF-8. IE doesn't + support the ISO-8859-1 encoding even if it is allowed by RFCs. + FIXME: par manque de caching, le bodystructure du message est redemandé pour accéder aux headers de cette pièce jointe. @@ -52,15 +60,20 @@ def getattachment(request): if type(disp[1][0]) != dict: atype, fieldname, filename = disp[0], disp[1][0], disp[1][1] else: - atype, fieldname, filename = disp[0], disp[1][0]['struct'][0], disp[1][0]['struct'][1] + atype, fieldname, filename = \ + disp[0], disp[1][0]['struct'][0], disp[1][0]['struct'][1] if fieldname.endswith('*'): cd = '%s; %s=%s' % (atype, fieldname, filename) else: cd = '%s; %s="%s"' % (atype, fieldname, filename) + fname = parse_headers(cd).filename_unsafe else: - cd = build_header(request.GET["fname"]) - resp["Content-Disposition"] = cd - resp["Content-Length"] = partdef["size"] + fname = request.GET.get("fname", None) + if fname is None: + raise WebmailError(_("Invalid request")) + resp["Content-Disposition"] = build_header(fname) + if int(partdef["size"]) < 200: + resp["Content-Length"] = partdef["size"] return resp From cb7267c66ea30ee43a1e55d5a7266a3808e8f90a Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Wed, 25 Sep 2013 22:04:20 +0200 Subject: [PATCH 2/6] The fix may be easier than expected :p see #473 --- modoboa/extensions/webmail/views.py | 31 +++-------------------------- 1 file changed, 3 insertions(+), 28 deletions(-) diff --git a/modoboa/extensions/webmail/views.py b/modoboa/extensions/webmail/views.py index 30c3055c6..cf806f129 100644 --- a/modoboa/extensions/webmail/views.py +++ b/modoboa/extensions/webmail/views.py @@ -1,6 +1,6 @@ # coding: utf-8 import os -from rfc6266 import build_header, parse_headers +from rfc6266 import build_header from django.conf import settings from django.http import HttpResponse from django.template import Template, Context @@ -29,14 +29,6 @@ from templatetags import webextras def getattachment(request): """Fetch a message attachment - The tricky part here consists in properly format the - *Content-Disposition* header. We use the rfc6266 package to encode - headers according to RFC5987 and RFC6266. - - Even if the header received from Dovecot is already encoded, we - decode and re-encode it to make sure it uses UTF-8. IE doesn't - support the ISO-8859-1 encoding even if it is allowed by RFCs. - FIXME: par manque de caching, le bodystructure du message est redemandé pour accéder aux headers de cette pièce jointe. @@ -45,7 +37,8 @@ def getattachment(request): mbox = request.GET.get("mbox", None) mailid = request.GET.get("mailid", None) pnum = request.GET.get("partnumber", None) - if not mbox or not mailid or not pnum: + fname = request.GET.get("fname", None) + if not mbox or not mailid or not pnum or not fname: raise WebmailError(_("Invalid request")) imapc = get_imapconnector(request) @@ -53,24 +46,6 @@ def getattachment(request): resp = HttpResponse(decode_payload(partdef["encoding"], payload)) resp["Content-Type"] = partdef["Content-Type"] resp["Content-Transfer-Encoding"] = partdef["encoding"] - if partdef["disposition"] != 'NIL': - disp = partdef["disposition"] - # FIXME : ugly hack, see fetch_parser.py for more explanation - # :p - if type(disp[1][0]) != dict: - atype, fieldname, filename = disp[0], disp[1][0], disp[1][1] - else: - atype, fieldname, filename = \ - disp[0], disp[1][0]['struct'][0], disp[1][0]['struct'][1] - if fieldname.endswith('*'): - cd = '%s; %s=%s' % (atype, fieldname, filename) - else: - cd = '%s; %s="%s"' % (atype, fieldname, filename) - fname = parse_headers(cd).filename_unsafe - else: - fname = request.GET.get("fname", None) - if fname is None: - raise WebmailError(_("Invalid request")) resp["Content-Disposition"] = build_header(fname) if int(partdef["size"]) < 200: resp["Content-Length"] = partdef["size"] From 717ae951494f137e922fda047849dee186e41ed3 Mon Sep 17 00:00:00 2001 From: Alberto Berti Date: Fri, 27 Sep 2013 00:08:47 +0200 Subject: [PATCH 3/6] Updated italian translation --- modoboa/locale/it/LC_MESSAGES/django.po | 102 +++++++++------------- modoboa/locale/it/LC_MESSAGES/djangojs.po | 40 +++------ 2 files changed, 51 insertions(+), 91 deletions(-) diff --git a/modoboa/locale/it/LC_MESSAGES/django.po b/modoboa/locale/it/LC_MESSAGES/django.po index 36373ed17..dcf26d52d 100644 --- a/modoboa/locale/it/LC_MESSAGES/django.po +++ b/modoboa/locale/it/LC_MESSAGES/django.po @@ -7,9 +7,9 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-09-20 22:30+0200\n" -"PO-Revision-Date: 2013-06-28 17:03+0200\n" -"Last-Translator: Giancarlo Gaifas \n" +"POT-Creation-Date: 2013-09-26 23:42+0200\n" +"PO-Revision-Date: 2013-09-27 00:07+0200\n" +"Last-Translator: Alberto Berti \n" "Language-Team: ITALIANO \n" "Language: it\n" "MIME-Version: 1.0\n" @@ -252,7 +252,7 @@ msgstr "" #: admin/app_settings.py:213 msgid "Key must be either 16, 24, or 32 bytes long" -msgstr "" +msgstr "La chiave può essere lunga solo 16, 24 o 32 bytes" #: admin/app_settings.py:222 msgid "Invalid syntax" @@ -520,12 +520,12 @@ msgstr "Non puoi importare un account con un ruolo superiore al tuo" #: admin/models.py:506 #, python-format msgid "The simple user '%s' must have a valid email address" -msgstr "" +msgstr "L'utente '%s' deve avere un indirizzo email valido" #: admin/models.py:510 #, python-format msgid "username and email fields must not differ for '%s'" -msgstr "" +msgstr "I campi nome utente e email di '%s' devono avere lo stesso valore" #: admin/models.py:534 #, python-format @@ -685,7 +685,7 @@ msgstr "Nuovo dominio" #: admin/views.py:89 admin/views.py:98 admin/views.py:228 admin/views.py:444 #: admin/views.py:451 extensions/sievefilters/views.py:129 -#: extensions/sievefilters/views.py:198 extensions/webmail/views.py:136 +#: extensions/sievefilters/views.py:198 extensions/webmail/views.py:128 msgid "Create" msgstr "Crea" @@ -707,7 +707,7 @@ msgstr "Dominio creato" #: admin/views.py:146 admin/views.py:285 admin/views.py:498 #: extensions/postfix_autoreply/templates/postfix_autoreply/autoreply.html:9 -#: extensions/sievefilters/views.py:148 extensions/webmail/views.py:168 +#: extensions/sievefilters/views.py:148 extensions/webmail/views.py:160 #: userprefs/templates/userprefs/forward.html:8 #: userprefs/templates/userprefs/profile.html:8 msgid "Update" @@ -770,11 +770,11 @@ msgstr "Alias modificato" #: admin/views.py:402 admin/views.py:545 admin/views.py:550 #: extensions/amavis/views.py:204 extensions/amavis/views.py:210 #: extensions/amavis/views.py:243 extensions/amavis/views.py:247 -#: extensions/amavis/views.py:249 extensions/webmail/views.py:41 -#: extensions/webmail/views.py:68 extensions/webmail/views.py:81 -#: extensions/webmail/views.py:98 extensions/webmail/views.py:114 -#: extensions/webmail/views.py:200 extensions/webmail/views.py:427 -#: extensions/webmail/views.py:438 extensions/webmail/views.py:465 +#: extensions/amavis/views.py:249 extensions/webmail/views.py:42 +#: extensions/webmail/views.py:60 extensions/webmail/views.py:73 +#: extensions/webmail/views.py:90 extensions/webmail/views.py:106 +#: extensions/webmail/views.py:192 extensions/webmail/views.py:419 +#: extensions/webmail/views.py:430 extensions/webmail/views.py:457 #: lib/formutils.py:32 lib/formutils.py:37 msgid "Invalid request" msgstr "Richiesta non valida" @@ -1007,7 +1007,7 @@ msgid "Previously on Modoboa..." msgstr "Precedentemente su Modoboa ..." #: admin/templates/admin/logs.html:6 extensions/amavis/sql_listing.py:25 -#: extensions/webmail/lib.py:38 extensions/webmail/lib.py:411 +#: extensions/webmail/lib.py:38 extensions/webmail/lib.py:413 msgid "Date" msgstr "Data" @@ -1067,11 +1067,11 @@ msgstr "Impostazioni Modoboa" #: admin/templatetags/admin_extras.py:13 msgid "enabled_m" -msgstr "" +msgstr "Abilitato" #: admin/templatetags/admin_extras.py:13 msgid "enabled_f" -msgstr "" +msgstr "Abilitata" #: admin/templatetags/admin_extras.py:36 msgid "Modoboa" @@ -1112,9 +1112,9 @@ msgstr "Visualizza le identita del dominio" #: admin/templatetags/admin_extras.py:178 #: admin/templatetags/admin_extras.py:194 #: admin/templatetags/admin_extras.py:201 -#, fuzzy, python-format +#, python-format msgid "Delete %s?" -msgstr "Elimina" +msgstr "Eliminare %s?" #: admin/templatetags/admin_extras.py:223 msgid "Remove this permission" @@ -1382,7 +1382,7 @@ msgstr "Da" #: extensions/amavis/sql_listing.py:24 extensions/sievefilters/forms.py:68 #: extensions/webmail/forms.py:10 extensions/webmail/lib.py:36 -#: extensions/webmail/lib.py:409 templates/common/email_searchbar.html:6 +#: extensions/webmail/lib.py:411 templates/common/email_searchbar.html:6 msgid "Subject" msgstr "Oggetto" @@ -2217,88 +2217,87 @@ msgid "Cci" msgstr "Cci" #: extensions/webmail/imaputils.py:237 -#, fuzzy msgid "Failed to retrieve hierarchy delimiter" -msgstr "Recupero delle impostazioni di filtro fallito" +msgstr "Impossibile recuperare il delimitatore delle gerarchie" #: extensions/webmail/imaputils.py:278 #, python-format msgid "Connection to IMAP server failed: %s" msgstr "Connessione al server IMAP fallita: %s" -#: extensions/webmail/lib.py:358 +#: extensions/webmail/lib.py:360 msgid "wrote:" msgstr "scritto:" -#: extensions/webmail/lib.py:423 extensions/webmail/lib.py:427 +#: extensions/webmail/lib.py:425 extensions/webmail/lib.py:429 msgid "Original message" msgstr "Messaggio originale" -#: extensions/webmail/views.py:86 +#: extensions/webmail/views.py:78 #, python-format msgid "%(count)d message deleted" msgid_plural "%(count)d messages deleted" msgstr[0] "%(count)d messaggio cancellato" msgstr[1] "%(count)d messaggi cancellati" -#: extensions/webmail/views.py:103 +#: extensions/webmail/views.py:95 msgid "Unknown action" msgstr "Azione sconosciuta" -#: extensions/webmail/views.py:116 lib/email_listing.py:137 +#: extensions/webmail/views.py:108 lib/email_listing.py:137 msgid "Empty mailbox" msgstr "Casella di posta vuota" -#: extensions/webmail/views.py:133 +#: extensions/webmail/views.py:125 #: extensions/webmail/templatetags/webextras.py:170 msgid "Create a new mailbox" msgstr "Crea una nuova casella di posta" -#: extensions/webmail/views.py:149 +#: extensions/webmail/views.py:141 msgid "Mailbox created" msgstr "Casella di posta creata" -#: extensions/webmail/views.py:165 +#: extensions/webmail/views.py:157 msgid "Edit mailbox" msgstr "Modifica casella di posta" -#: extensions/webmail/views.py:180 +#: extensions/webmail/views.py:172 msgid "Mailbox updated" msgstr "Casella di posta aggiornata" -#: extensions/webmail/views.py:214 extensions/webmail/views.py:396 -#: extensions/webmail/views.py:505 +#: extensions/webmail/views.py:206 extensions/webmail/views.py:388 +#: extensions/webmail/views.py:497 msgid "Bad request" msgstr "Richiesta sbagliata" -#: extensions/webmail/views.py:246 +#: extensions/webmail/views.py:238 msgid "Failed to save attachment: " msgstr "Salvataggio allegato fallito: " -#: extensions/webmail/views.py:249 +#: extensions/webmail/views.py:241 #, python-format msgid "Attachment is too big (limit: %s)" msgstr "L'allegato è troppo grande (limite: %s)" -#: extensions/webmail/views.py:255 +#: extensions/webmail/views.py:247 #: extensions/webmail/templates/webmail/compose.html:15 #: extensions/webmail/templates/webmail/headers.html:4 msgid "Attachments" msgstr "Allegati" -#: extensions/webmail/views.py:271 +#: extensions/webmail/views.py:263 msgid "Bad query" msgstr "Domanda sbagliata" -#: extensions/webmail/views.py:281 +#: extensions/webmail/views.py:273 msgid "Failed to remove attachment: " msgstr "Rimozione allegato fallita: " -#: extensions/webmail/views.py:286 +#: extensions/webmail/views.py:278 msgid "Unknown attachment" msgstr "Allegato sconosciuto" -#: extensions/webmail/views.py:501 +#: extensions/webmail/views.py:493 msgid "Undefined action" msgstr "Azione non definita" @@ -2455,11 +2454,11 @@ msgstr "È intervenuto un errore interno" #: templates/nlayout.html:62 msgid "Powered by Modoboa" -msgstr "" +msgstr "Powered by Modoboa" #: templates/common/email_searchbar.html:5 msgid "From address" -msgstr "Indirizzo from" +msgstr "Mittente" #: templates/common/email_searchbar.html:10 msgid "Both" @@ -2598,24 +2597,3 @@ msgstr "Aggiorna le informazioni personali" #: userprefs/templatetags/uprefs_extras.py:57 msgid "Logout" msgstr "Esci" - -#~ msgid "Failed to rename mailbox: %s" -#~ msgstr "Impossibile rinominare la casella di posta: %s" - -#~ msgid "Failed to remove mailbox: %s" -#~ msgstr "Impossibile rimuovere la casella di posta: %s" - -#~ msgid "Delete the domain" -#~ msgstr "Cancella il dominio" - -#~ msgid "Delete this account" -#~ msgstr "Cancella questo account" - -#~ msgid "Delete this distribution list" -#~ msgstr "Cancella l'elenco di smistamento" - -#~ msgid "Delete this forward" -#~ msgstr "Cancella questo inoltro" - -#~ msgid "Delete this alias" -#~ msgstr "Cancella questo alias" diff --git a/modoboa/locale/it/LC_MESSAGES/djangojs.po b/modoboa/locale/it/LC_MESSAGES/djangojs.po index 17330283a..086b29052 100644 --- a/modoboa/locale/it/LC_MESSAGES/djangojs.po +++ b/modoboa/locale/it/LC_MESSAGES/djangojs.po @@ -6,9 +6,9 @@ msgid "" msgstr "" "Project-Id-Version: Modoboa\n" -"Report-Msgid-Bugs-To: http://dev.modoboa.org\n" -"POT-Creation-Date: 2013-06-28 16:37+0200\n" -"PO-Revision-Date: 2013-06-28 16:58+0200\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2013-09-26 23:44+0200\n" +"PO-Revision-Date: 2013-09-27 00:07+0200\n" "Last-Translator: Giancarlo Gaifas \n" "Language-Team: Italiano \n" "Language: it\n" @@ -33,11 +33,9 @@ msgstr "Questa operazione eliminerà TUTTI i dati associati a questo dominio." msgid "" "This operation will remove all data associated to this domain, excepting " "accounts." -msgstr "Questa operazione eliminerà tutti i dati associati a questo dominio tranne gli account" - -#: admin/static/admin/js/admin.js:117 -msgid "Delete this domain?" -msgstr "Elimino questo dominio?" +msgstr "" +"Questa operazione eliminerà tutti i dati associati a questo dominio tranne " +"gli account" #: admin/static/admin/js/admin.js:161 msgid "No domain administrator defined" @@ -47,22 +45,6 @@ msgstr "Non è stato definito un amministratore di dominio" msgid "Do not delete mailbox directory" msgstr "Non eliminare la cartella della casella posta" -#: admin/static/admin/js/admin.js:243 -msgid "Delete this account?" -msgstr "Eliminare questo account" - -#: admin/static/admin/js/admin.js:249 -msgid "Delete this list?" -msgstr "Eliminare questo elenco?" - -#: admin/static/admin/js/admin.js:254 -msgid "Delete this forward?" -msgstr "Eliminare questo inoltro?" - -#: admin/static/admin/js/admin.js:259 -msgid "Delete this alias?" -msgstr "Eliminare questo alias?" - #: extensions/amavis/static/amavis/js/quarantine.js:126 msgid "Release this selection?" msgstr "Rilasciare questa selezione?" @@ -114,11 +96,11 @@ msgstr "attivo" msgid "Search a domain" msgstr "Cerca un dominio" -#: extensions/webmail/static/webmail/js/webmail.js:475 +#: extensions/webmail/static/webmail/js/webmail.js:476 msgid "Remove the selected mailbox?" msgstr "Rimuovere la casella di posta selezionata?" -#: extensions/webmail/static/webmail/js/webmail.js:489 +#: extensions/webmail/static/webmail/js/webmail.js:490 msgid "Mailbox removed" msgstr "Casella di posta rimossa" @@ -144,15 +126,15 @@ msgid_plural "Moving %s messages" msgstr[0] "Spostato %s messaggio" msgstr[1] "Spostati %s messaggi" -#: static/js/confirmation.js:48 +#: static/js/confirmation.js:50 msgid "Warning" msgstr "Attenzione" -#: static/js/confirmation.js:57 +#: static/js/confirmation.js:59 msgid "Cancel" msgstr "Elimina" -#: static/js/confirmation.js:99 static/js/history.js:239 +#: static/js/confirmation.js:101 static/js/history.js:235 msgid "Internal Error" msgstr "Errore interno" From 93a5fd5bb84cb29887de74d23049374c94f5b48a Mon Sep 17 00:00:00 2001 From: ricardopadilha Date: Fri, 27 Sep 2013 11:24:07 +0200 Subject: [PATCH 4/6] Update deploy.py to support sqlite3 modoboa-admin.py does not allow giving "sqlite3" as a database type. --- modoboa/core/commands/deploy.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modoboa/core/commands/deploy.py b/modoboa/core/commands/deploy.py index 3c9c15107..cd33fde07 100644 --- a/modoboa/core/commands/deploy.py +++ b/modoboa/core/commands/deploy.py @@ -75,8 +75,8 @@ class DeployCommand(Command): """ print "Configuring database connection: %s" % name info = {'conn_name': name, - 'dbtype': raw_input('Database type (mysql or postgres): ')} - if info['dbtype'] not in ['mysql', 'postgres']: + 'dbtype': raw_input('Database type (mysql, postgres or sqlite3): ')} + if info['dbtype'] not in ['mysql', 'postgres', 'sqlite3']: info['dbtype'] = 'mysql' if info['dbtype'] == 'postgres': info['dbtype'] = 'postgresql_psycopg2' From bab49c812dfd80a41e8a0429dda52fc05e37710f Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Fri, 27 Sep 2013 15:48:12 +0200 Subject: [PATCH 5/6] Swedish translation updated. --- modoboa/locale/sv/LC_MESSAGES/django.po | 215 ++++++------------------ 1 file changed, 54 insertions(+), 161 deletions(-) diff --git a/modoboa/locale/sv/LC_MESSAGES/django.po b/modoboa/locale/sv/LC_MESSAGES/django.po index 7d8e66cac..8373e44ff 100644 --- a/modoboa/locale/sv/LC_MESSAGES/django.po +++ b/modoboa/locale/sv/LC_MESSAGES/django.po @@ -1,22 +1,22 @@ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. -# +# # Translators: # ogg1980 , 2013 +# ogg1980 , 2013 msgid "" msgstr "" "Project-Id-Version: Modoboa\n" -"Report-Msgid-Bugs-To: \n" +"Report-Msgid-Bugs-To: http://dev.modoboa.org/\n" "POT-Creation-Date: 2013-09-20 22:30+0200\n" -"PO-Revision-Date: 2013-06-01 14:36+0000\n" -"Last-Translator: Antoine Nguyen \n" -"Language-Team: Swedish (http://www.transifex.com/projects/p/modoboa/language/" -"sv/)\n" -"Language: sv\n" +"PO-Revision-Date: 2013-09-27 08:19+0000\n" +"Last-Translator: ogg1980 \n" +"Language-Team: Swedish (http://www.transifex.com/projects/p/modoboa/language/sv/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" +"Language: sv\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: admin/__init__.py:9 @@ -122,9 +122,7 @@ msgstr "Bind DN" msgid "" "The distinguished name to use when binding to the LDAP server. Leave empty " "for an anonymous bind" -msgstr "" -"Det unika namnet för att använda vid bindningar till LDAP-servern. Lämna " -"tomt för en anonym bindning" +msgstr "Det unika namnet för att använda vid bindningar till LDAP-servern. Lämna tomt för en anonym bindning" #: admin/app_settings.py:97 msgid "Bind password" @@ -132,8 +130,7 @@ msgstr "Bind lösenord" #: admin/app_settings.py:99 msgid "The password to use when binding to the LDAP server (with 'Bind DN')" -msgstr "" -"Lösenordet ska användas vid bindning till LDAP-servern (med \"Bind DN ')" +msgstr "Lösenordet ska användas vid bindning till LDAP-servern (med \"Bind DN ')" #: admin/app_settings.py:105 msgid "Search base" @@ -151,9 +148,7 @@ msgstr "Sökfilter" msgid "" "An optional filter string (e.g. '(objectClass=person)'). In order to be " "valid, it must be enclosed in parentheses." -msgstr "" -"En valfri filtersträng (t.ex. \"(objectClass = personer) '). För att vara " -"giltigt måste det omges av parenteser." +msgstr "En valfri filtersträng (t.ex. \"(objectClass = personer) '). För att vara giltigt måste det omges av parenteser." #: admin/app_settings.py:119 msgid "User DN template" @@ -164,9 +159,7 @@ msgstr "Anvädar DN mall" msgid "" "The template used to construct a user's DN. It should contain one " "placeholder (ie. %(user)s)" -msgstr "" -"Mallen som används för att konstruera en användares DN. Den bör innehålla en " -"platshållare (dvs. %(user)s)" +msgstr "Mallen som används för att konstruera en användares DN. Den bör innehålla en platshållare (dvs. %(user)s)" #: admin/app_settings.py:126 msgid "Password attribute" @@ -199,9 +192,7 @@ msgstr "Hantera brevlådor på filsystem" msgid "" "Rename or remove mailboxes on the filesystem when they get renamed or " "removed within Modoboa" -msgstr "" -"Byt namn på eller ta bort brevlådor på filsystemet när de kommer byta namn " -"eller tas bort inom Modoboa" +msgstr "Byt namn på eller ta bort brevlådor på filsystemet när de kommer byta namn eller tas bort inom Modoboa" #: admin/app_settings.py:146 msgid "Mailboxes ower" @@ -249,12 +240,11 @@ msgstr "Standard omdirigering när ingen applikation angetts" #: admin/app_settings.py:213 msgid "Key must be either 16, 24, or 32 bytes long" -msgstr "" +msgstr "Nyckel måste vara endera 16, 24 eller 32 bytes lång" #: admin/app_settings.py:222 -#, fuzzy msgid "Invalid syntax" -msgstr "Ogiltig rad" +msgstr "Ogiltig syntax" #: admin/app_settings.py:245 msgid "This field is required" @@ -268,9 +258,7 @@ msgstr "Alias" msgid "" "Alias(es) of this domain. Indicate only one name per input, press ENTER to " "add a new input." -msgstr "" -"Alias för denna domän. Ange endast ett namn per rad, tryck ENTER för att " -"lägga till en rad." +msgstr "Alias för denna domän. Ange endast ett namn per rad, tryck ENTER för att lägga till en rad." #: admin/forms.py:59 msgid "An alias with this name already exists" @@ -301,9 +289,7 @@ msgstr "Namn" msgid "" "The administrator's name. Don't include the domain's name here, it will be " "automatically appended." -msgstr "" -"Administratörens namn. Inkludera inte domänens namn här, den kommer att " -"läggas till automatiskt." +msgstr "Administratörens namn. Inkludera inte domänens namn här, den kommer att läggas till automatiskt." #: admin/forms.py:160 msgid "Create aliases" @@ -330,9 +316,7 @@ msgstr "E-post adress" msgid "" "The distribution list address. Use the '*' character to create a 'catchall' " "address (ex: *@domain.tld)." -msgstr "" -"Distributionslistans adress. Använd '*' för att skapa en 'catchall' adress " -"(ex: *@domain.tld)." +msgstr "Distributionslistans adress. Använd '*' för att skapa en 'catchall' adress (ex: *@domain.tld)." #: admin/forms.py:239 msgid "Recipients" @@ -342,9 +326,7 @@ msgstr "Mottagare" msgid "" "Mailbox(es) this alias will point to. Indicate only one address per input, " "press ENTER to add a new input." -msgstr "" -"Brevlåda/or detta alias kommer att peka på. Ange endast en adress per " -"inlägg, tryck ENTER för att lägga till en ny inlägg." +msgstr "Brevlåda/or detta alias kommer att peka på. Ange endast en adress per inlägg, tryck ENTER för att lägga till en ny inlägg." #: admin/forms.py:278 admin/forms.py:542 admin/forms.py:568 msgid "Domain does not exist" @@ -454,20 +436,14 @@ msgid "" "Quota in MB for this mailbox. Define a custom value or use domain's default " "one. Leave empty to define an unlimited value (not allowed for domain " "administrators)." -msgstr "" -"Kvot i MB för denna brevlåda. Definiera ett anpassat värde eller använda " -"domänens förvalda. Lämna tomt för att definiera ett obegränsat värde (ej " -"tillåtet för domänadministratörer)." +msgstr "Kvot i MB för denna brevlåda. Definiera ett anpassat värde eller använda domänens förvalda. Lämna tomt för att definiera ett obegränsat värde (ej tillåtet för domänadministratörer)." #: admin/forms.py:499 msgid "" "Alias(es) of this mailbox. Indicate only one address per input, press ENTER " "to add a new input. Use the '*' character to create a 'catchall' alias (ex: " "*@domain.tld)." -msgstr "" -"Alias för denna brevlåda. Ange endast en adress per rad, tryck ENTER för att " -"lägga till ny rad. Använd '*' för att skapa ett 'catchall' alias (ex: " -"*@domain.tld)." +msgstr "Alias för denna brevlåda. Ange endast en adress per rad, tryck ENTER för att lägga till ny rad. Använd '*' för att skapa ett 'catchall' alias (ex: *@domain.tld)." #: admin/forms.py:550 #, python-format @@ -518,12 +494,12 @@ msgstr "Du kan inte importera konto med en roll större än din" #: admin/models.py:506 #, python-format msgid "The simple user '%s' must have a valid email address" -msgstr "" +msgstr "Den enkla användaren '%s' måste ha en gällande e-postadress" #: admin/models.py:510 #, python-format msgid "username and email fields must not differ for '%s'" -msgstr "" +msgstr "användarnamn och e-post fält får inte avvika för '%s'" #: admin/models.py:534 #, python-format @@ -596,11 +572,9 @@ msgstr "En kvota krävs" #: admin/models.py:1032 msgid "" -"The alias address (without the domain part). For a 'catch-all' address, just " -"enter an * character." -msgstr "" -"Alias adress (utan domän delen). För en \"catch-all\"-adress, ange bara " -"asterisk *." +"The alias address (without the domain part). For a 'catch-all' address, just" +" enter an * character." +msgstr "Alias adress (utan domän delen). För en \"catch-all\"-adress, ange bara asterisk *." #: admin/models.py:1036 msgid "mailboxes" @@ -632,9 +606,9 @@ msgid "Invalid line: %s" msgstr "Ogiltig rad: %s" #: admin/models.py:1175 -#, fuzzy, python-format +#, python-format msgid "Domain '%s' does not exist" -msgstr "Domän existerar inte" +msgstr "Domänen '%s' existerar inte" #: admin/models.py:1218 msgid "Check to enable this extension" @@ -817,29 +791,15 @@ msgstr "%d objekt importerade" msgid "" "Provide a CSV file where lines respect one of the following formats:\n" "
    \n" -"
  • account; loginname; password; first name; last name; enabled; group; " -"address[, domain, ...]
  • \n" +"
  • account; loginname; password; first name; last name; enabled; group; address[, domain, ...]
  • \n" "
  • alias; address; enabled; internal recipient
  • \n" "
  • forward; address; enabled; external recipient
  • \n" "
  • dlist; address; enabled; recipient; recipient; ...
  • \n" "
\n" -"

The first element of each line is mandatory and must be equal to one of " -"the previous values.

\n" +"

The first element of each line is mandatory and must be equal to one of the previous values.

\n" "\n" "

You can use a different character as separator.

\n" -msgstr "" -"Tillhandahåll en CSV-fil där raderna har följande format:⏎\n" -"
    ⏎\n" -"
  • account; loginname; password; first name; last name; enabled; group; " -"address[, domain, ...]
  • ⏎\n" -"
  • alias; address; enabled; internal recipient
  • ⏎\n" -"
  • forward; address; enabled; external recipient
  • ⏎\n" -"
  • dlist; address; enabled; recipient; recipient; ...
  • ⏎\n" -"
⏎\n" -"

Det första elementet i varje rad är obligatorisk och måste vara lika med " -"en av de tidigare värdena.

⏎\n" -"⏎\n" -"

Du kan använda ett annat tecken separator.

⏎\n" +msgstr "Tillhandahåll en CSV-fil där raderna har följande format:⏎\n
    ⏎\n
  • account; loginname; password; first name; last name; enabled; group; address[, domain, ...]
  • ⏎\n
  • alias; address; enabled; internal recipient
  • ⏎\n
  • forward; address; enabled; external recipient
  • ⏎\n
  • dlist; address; enabled; recipient; recipient; ...
  • ⏎\n
⏎\n

Det första elementet i varje rad är obligatorisk och måste vara lika med en av de tidigare värdena.

⏎\n⏎\n

Du kan använda ett annat tecken separator.

⏎\n" #: admin/views.py:775 msgid "Import identities" @@ -866,18 +826,9 @@ msgid "" "
  • domain; name; quota; enabled
  • \n" "
  • domainalias; name; targeted domain; enabled
  • \n" "\n" -"

    The first element of each line is mandatory and must be equal to one of " -"the previous values.

    \n" +"

    The first element of each line is mandatory and must be equal to one of the previous values.

    \n" "

    You can use a different character as separator.

    \n" -msgstr "" -"Tillhandahåll en CSV fil där raderna har följande format:\n" -"
      \n" -"
    • domän, namn, kvot, aktiverade\n" -"
    • domainalias, namn, riktad domän, aktiverade\n" -"\n" -"

      det första elementet i varje rad är obligatoriskt och måste vara lika med " -"en av de tidigare värdena.\n" -"

      Du kan använda ett annat tecken som separator.\n" +msgstr "Tillhandahåll en CSV fil där raderna har följande format:\n

        \n
      • domän, namn, kvot, aktiverade\n
      • domainalias, namn, riktad domän, aktiverade\n\n

        det första elementet i varje rad är obligatoriskt och måste vara lika med en av de tidigare värdena.\n

        Du kan använda ett annat tecken som separator.\n" #: admin/views.py:845 msgid "Import domains" @@ -1108,9 +1059,9 @@ msgstr "Visa domänens konton" #: admin/templatetags/admin_extras.py:178 #: admin/templatetags/admin_extras.py:194 #: admin/templatetags/admin_extras.py:201 -#, fuzzy, python-format +#, python-format msgid "Delete %s?" -msgstr "Radera" +msgstr "Radera %s?" #: admin/templatetags/admin_extras.py:223 msgid "Remove this permission" @@ -1159,30 +1110,15 @@ msgid "" "\n" "

        Welcome to the online demo!

        \n" "

        Warning: the demo's concent is resetted every two hours.

        \n" -"

        To try the demo, the following users are available (username/password):

        \n" +"

        To try the demo, the following users are available (username/password):

        \n" "
          \n" "
        • A super administrator : admin/password
        • \n" "
        • A domain administrator : admin@demo.local/admin
        • \n" "
        • A simple user : user@demo.local/user
        • \n" "
        \n" "

        Please report any issue or give any suggestion to \n" -" the " -"mailing list.

        \n" -msgstr "" -"\n" -"

        Välkommen till online-demot!

        \n" -"

        Varning: demots innehåll återställs varannan timme

        \n" -"

        För att prova demot, följande användare finns tillgängliga " -"(användarnamn/lösenord):

        \n" -"
          \n" -"
        • En super administratör: admin/password
        • \n" -"
        • En domän administratölr: admin@demo.local/admin
        • \n" -"
        • En enkel användare: user@demo.local/user
        • \n" -"
        \n" -"

        Var vänlig rapportera eventuella fel eller ge förslag på mailinglistan." -"

        \n" +" the mailing list.

        \n" +msgstr "\n

        Välkommen till online-demot!

        \n

        Varning: demots innehåll återställs varannan timme

        \n

        För att prova demot, följande användare finns tillgängliga (användarnamn/lösenord):

        \n
          \n
        • En super administratör: admin/password
        • \n
        • En domän administratölr: admin@demo.local/admin
        • \n
        • En enkel användare: user@demo.local/user
        • \n
        \n

        Var vänlig rapportera eventuella fel eller ge förslag på mailinglistan.

        \n" #: extensions/amavis/__init__.py:22 msgid "Simple amavis management frontend" @@ -1333,9 +1269,7 @@ msgstr "standard" #: extensions/amavis/models.py:98 msgid "Bypass virus checks or not. Choose 'default' to use global settings." -msgstr "" -"Kringgå virus kontroller eller ej. Välj \"standard\" för att använda globala " -"inställningar." +msgstr "Kringgå virus kontroller eller ej. Välj \"standard\" för att använda globala inställningar." #: extensions/amavis/models.py:101 msgid "Spam filter" @@ -1343,9 +1277,7 @@ msgstr "Skräp filter" #: extensions/amavis/models.py:106 msgid "Bypass spam checks or not. Choose 'default' to use global settings." -msgstr "" -"Kringgå skräp filter eller ej. Välj 'standard' för att använda globala " -"inställningar." +msgstr "Kringgå skräp filter eller ej. Välj 'standard' för att använda globala inställningar." #: extensions/amavis/models.py:109 msgid "Banned filter" @@ -1353,9 +1285,7 @@ msgstr "Förbjudna filter" #: extensions/amavis/models.py:114 msgid "Bypass banned checks or not. Choose 'default' to use global settings." -msgstr "" -"Kringgå förbjudna filter eller inte. Välj 'standard' för att använda globala " -"inställningar." +msgstr "Kringgå förbjudna filter eller inte. Välj 'standard' för att använda globala inställningar." #: extensions/amavis/models.py:144 msgid "Spam marker" @@ -1363,11 +1293,9 @@ msgstr "Skräp markering" #: extensions/amavis/models.py:146 msgid "" -"Modify spam subject using the specified text. Choose 'default' to use global " -"settings." -msgstr "" -"Ändra skräppost ämnesrad med vald text. Välj 'standard' för att använd " -"globala inställningar." +"Modify spam subject using the specified text. Choose 'default' to use global" +" settings." +msgstr "Ändra skräppost ämnesrad med vald text. Välj 'standard' för att använd globala inställningar." #: extensions/amavis/sql_listing.py:23 extensions/webmail/lib.py:37 msgid "From" @@ -1450,12 +1378,8 @@ msgid "" msgid_plural "" "\n" "%(counter)s release requests are pending for action.\n" -msgstr[0] "" -"\n" -"%(counter)s frisläpps förfrågning väntar på åtgärd.\n" -msgstr[1] "" -"\n" -"%(counter)s frisläpps förfrågningar väntar på åtgärd.'\n" +msgstr[0] "\n%(counter)s frisläpps förfrågning väntar på åtgärd.\n" +msgstr[1] "\n%(counter)s frisläpps förfrågningar väntar på åtgärd.'\n" #: extensions/amavis/templates/amavis/notifications/pending_requests.html:6 msgid "Sketch:" @@ -1530,8 +1454,7 @@ msgstr "Dålig MIME" #: extensions/limits/__init__.py:23 msgid "" "Per administrator resources to limit the number of objects they can create" -msgstr "" -"Per administratör resurser för att begränsa antalet objekt som de kan skapa" +msgstr "Per administratör resurser för att begränsa antalet objekt som de kan skapa" #: extensions/limits/__init__.py:54 msgid "Limits" @@ -1555,7 +1478,8 @@ msgid "Domain admins" msgstr "Domänadministratörer" #: extensions/limits/app_settings.py:15 -msgid "Maximum number of allowed domain administrators for a new administrator" +msgid "" +"Maximum number of allowed domain administrators for a new administrator" msgstr "Maximalt antal domänadministratörer den här användaren kan skapa" #: extensions/limits/app_settings.py:21 @@ -1804,9 +1728,7 @@ msgstr "Omdirigera meddelande till" #: extensions/sievefilters/lib.py:37 msgid "Connection to MANAGESIEVE server failed, check your configuration" -msgstr "" -"Anslutning till MANAGESIEVE servern misslyckades, kontrollera din " -"konfiguration" +msgstr "Anslutning till MANAGESIEVE servern misslyckades, kontrollera din konfiguration" #: extensions/sievefilters/lib.py:82 msgid "Not enough space on server" @@ -1885,9 +1807,7 @@ msgstr "aktivera" #: extensions/sievefilters/templates/sievefilters/index.html:74 msgid "" "No filters set defined yet. You must create one in order to define filters." -msgstr "" -"Inga filter uppsättningar definierade. Du måste skapa en uppsättning för att " -"kunna definiera filter." +msgstr "Inga filter uppsättningar definierade. Du måste skapa en uppsättning för att kunna definiera filter." #: extensions/sievefilters/templatetags/sfilters_tags.py:16 msgid "New filters set" @@ -2200,9 +2120,8 @@ msgid "Cci" msgstr "BCC" #: extensions/webmail/imaputils.py:237 -#, fuzzy msgid "Failed to retrieve hierarchy delimiter" -msgstr "Det gick inte att hämta filteruppsättning" +msgstr "Det gick inte att hämta hierarki avgränsare" #: extensions/webmail/imaputils.py:278 #, python-format @@ -2221,8 +2140,8 @@ msgstr "Orginalmeddelande" #, python-format msgid "%(count)d message deleted" msgid_plural "%(count)d messages deleted" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "%(count)d meddelande raderat" +msgstr[1] "%(count)d meddelanden raderade" #: extensions/webmail/views.py:103 msgid "Unknown action" @@ -2530,9 +2449,7 @@ msgstr "Vidarebefordra meddelande och spara kopia i din lokala brevlåda" msgid "" "You can't define a forward to a local destination. Please ask your " "administrator to create an alias instead." -msgstr "" -"Du kan inte definiera en vidarebefordring till en lokal destination. Be din " -"administratör att skapa ett alias istället." +msgstr "Du kan inte definiera en vidarebefordring till en lokal destination. Be din administratör att skapa ett alias istället." #: userprefs/views.py:32 msgid "You need a mailbox in order to define a forward" @@ -2580,27 +2497,3 @@ msgstr "Uppdatera personlig information" #: userprefs/templatetags/uprefs_extras.py:57 msgid "Logout" msgstr "Logga ut" - -#~ msgid "Failed to rename mailbox: %s" -#~ msgstr "Kunde byta namn på brevlåda: %s" - -#~ msgid "Failed to remove mailbox: %s" -#~ msgstr "Kunde inte radera brevlåda: %s" - -#~ msgid "Mailbox %s does not exist" -#~ msgstr "Brevlådan %s finns inte" - -#~ msgid "Delete the domain" -#~ msgstr "Radera domänen" - -#~ msgid "Delete this account" -#~ msgstr "Radera detta konto" - -#~ msgid "Delete this distribution list" -#~ msgstr "Radera denna distributionslista" - -#~ msgid "Delete this forward" -#~ msgstr "Radera denna vidarebefodring" - -#~ msgid "Delete this alias" -#~ msgstr "Radera detta alia" From 8bb753e89003309a67370419a9c5cb6c0f9bf28b Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Fri, 27 Sep 2013 20:10:47 +0200 Subject: [PATCH 6/6] SQLite3 support. fixes #484 --- README.rst | 4 +- doc/integration/imap_and_smtp.rst | 39 ++++++--- modoboa/core/commands/postfix_maps.py | 111 +++++++++++++++++--------- 3 files changed, 106 insertions(+), 48 deletions(-) diff --git a/README.rst b/README.rst index 7c17f005f..d98c1dd09 100644 --- a/README.rst +++ b/README.rst @@ -11,8 +11,8 @@ as an administration panel or a webmail. Modoboa integrates with well known software such as `Postfix `_ or `Dovecot `_. A SQL database (`MySQL `_, `PostgreSQL -`_) is used as a central point of -communication between all components. +`_ or `SQLite `_) +is used as a central point of communication between all components. Modoboa is developed with modularity in mind, expanding it is really easy. Actually, all current features are extensions. diff --git a/doc/integration/imap_and_smtp.rst b/doc/integration/imap_and_smtp.rst index 3838c29ba..e2c28e187 100644 --- a/doc/integration/imap_and_smtp.rst +++ b/doc/integration/imap_and_smtp.rst @@ -161,6 +161,23 @@ PostgreSQL users iterate_query = SELECT email AS username FROM admin_user +SQLite users +------------ + +:: + + driver = sqlite + + connect = + + default_pass_scheme = CRYPT + + password_query = SELECT email AS user, password FROM admin_user WHERE email='%u' and is_active=1 + + user_query = SELECT '/%d/%n' AS home, as uid, as gid, ('*:bytes=' || mb.quota || 'M') AS quota_rule FROM admin_mailbox mb INNER JOIN admin_domain dom ON mb.domain_id=dom.id WHERE mb.address='%n' AND dom.name='%d' + + iterate_query = SELECT email AS username FROM admin_user + .. note:: Replace values between ``<>`` with yours. @@ -228,6 +245,8 @@ Finally, edit the :file:`dovecot-dict-sql.conf.ext` file and put the following content inside:: connect = host= dbname= user= password= + # SQLite users + # connect = /path/to/the/database.db map { pattern = priv/quota/storage @@ -404,7 +423,7 @@ be used by Postfix to lookup into Modoboa tables. To automaticaly generate the requested map files and store them in a directory, run the following command:: - $ modoboa-admin.py postfix_maps --dbtype mapfiles + $ modoboa-admin.py postfix_maps --dbtype mapfiles :file:`mapfiles` is the directory where the files will be stored. Answer the few questions and you're done. @@ -427,22 +446,24 @@ Use the following configuration in the :file:`/etc/postfix/main.cf` file virtual_mailbox_base = /var/vmail relay_domains = - virtual_mailbox_domains = mysql:/etc/postfix/sql-domains.cf - virtual_alias_domains = mysql:/etc/postfix/sql-domain-aliases.cf - virtual_mailbox_maps = mysql:/etc/postfix/sql-mailboxes.cf - virtual_alias_maps = mysql:/etc/postfix/sql-aliases.cf, - mysql:/etc/postfix/sql-domain-aliases-mailboxes.cf, - mysql:/etc/postfix/sql-email2email.cf, - mysql:/etc/postfix/sql-catchall-aliases.cf + virtual_mailbox_domains = :/etc/postfix/sql-domains.cf + virtual_alias_domains = :/etc/postfix/sql-domain-aliases.cf + virtual_mailbox_maps = :/etc/postfix/sql-mailboxes.cf + virtual_alias_maps = :/etc/postfix/sql-aliases.cf, + :/etc/postfix/sql-domain-aliases-mailboxes.cf, + :/etc/postfix/sql-email2email.cf, + :/etc/postfix/sql-catchall-aliases.cf smtpd_recipient_restrictions = ... - check_recipient_access mysql:/etc/postfix/maps/sql-maintain.cf + check_recipient_access :/etc/postfix/maps/sql-maintain.cf permit_mynetworks ... # Stuff after +Replace ```` by the name of the database you use. + Then, edit the :file:`/etc/postfix/master.cf` file and add the following definition at the end:: diff --git a/modoboa/core/commands/postfix_maps.py b/modoboa/core/commands/postfix_maps.py index afe3b57d5..e8c0e549a 100644 --- a/modoboa/core/commands/postfix_maps.py +++ b/modoboa/core/commands/postfix_maps.py @@ -6,69 +6,111 @@ from django.template import Context, Template from modoboa.core.management import Command -class MapFile(object): - category = 'std' +class MapFilesGenerator(object): def __init__(self): - self.base_tpl = Template("""user = {{ dbuser }} -password = {{ dbpassword }} + self.template = Template("""user = {{ dbuser }} +password = {{ dbpass }} dbname = {{ dbname }} hosts = {{ dbhost }} query = {{ query|safe }} """) + self.tpl_context = { + 'dbhost': raw_input('Database host: '), + 'dbname': raw_input('Database name: '), + 'dbuser': raw_input('Username: '), + 'dbpass': getpass.getpass('Password: ') + } + if not self.tpl_context['dbhost']: + self.tpl_context['dbhost'] = '127.0.0.1' - def render(self, destdir, dbtype, **options): + def render(self, args): + """Render all map files. + + :param args: arguments received from the command line + """ try: - options['query'] = getattr(self, dbtype) - except AttributeError: - return + os.mkdir(args.destdir) + except OSError: + pass - content = self.base_tpl.render(Context(options)) - fp = open("%s/%s" % (destdir, self.filename), "w") - fp.write(content) - fp.close() + for v in globals().values(): + if not inspect.isclass(v) or not issubclass(v, MapFile): + continue + if v is MapFile: + continue + if not v.category in args.categories: + continue + content = self.template.render( + Context(dict(self.tpl_context.items(), + query=getattr(v, args.dbtype))) + ) + with open("%s/%s" % (args.destdir, v.filename), "w") as fp: + print >> fp, content + + +class SQLiteMapFilesGenerator(MapFilesGenerator): + + def __init__(self): + self.template = Template("""dbpath = {{ dbpath }} +query = {{ query|safe }} +""") + self.tpl_context = { + 'dbpath': raw_input('Database path: ') + } + + +class MapFile(object): + category = 'std' class DomainsMap(MapFile): filename = 'sql-domains.cf' mysql = "SELECT name FROM admin_domain WHERE name='%s' AND enabled=1" postgres = "SELECT name FROM admin_domain WHERE name='%s' AND enabled" + sqlite = "SELECT name FROM admin_domain WHERE name='%s' AND enabled=1" class DomainsAliasesMap(MapFile): filename = 'sql-domain-aliases.cf' mysql = "SELECT dom.name FROM admin_domain dom INNER JOIN admin_domainalias domal ON dom.id=domal.target_id WHERE domal.name='%s' AND domal.enabled=1 AND dom.enabled=1" postgres = "SELECT dom.name FROM admin_domain dom INNER JOIN admin_domainalias domal ON dom.id=domal.target_id WHERE domal.name='%s' AND domal.enabled AND dom.enabled" + sqlite = "SELECT dom.name FROM admin_domain dom INNER JOIN admin_domainalias domal ON dom.id=domal.target_id WHERE domal.name='%s' AND domal.enabled=1 AND dom.enabled=1" class MailboxesMap(MapFile): filename = 'sql-mailboxes.cf' mysql = "SELECT 1 FROM admin_mailbox mb INNER JOIN admin_domain dom ON mb.domain_id=dom.id INNER JOIN admin_user user ON mb.user_id=user.id WHERE dom.enabled=1 AND dom.name='%d' AND user.is_active=1 AND mb.address='%u'" postgres = "SELECT 1 FROM admin_mailbox mb INNER JOIN admin_domain dom ON mb.domain_id=dom.id INNER JOIN admin_user u ON mb.user_id=u.id WHERE dom.enabled AND dom.name='%d' AND u.is_active AND mb.address='%u'" + sqlite = "SELECT 1 FROM admin_mailbox mb INNER JOIN admin_domain dom ON mb.domain_id=dom.id INNER JOIN admin_user user ON mb.user_id=user.id WHERE dom.enabled=1 AND dom.name='%d' AND user.is_active=1 AND mb.address='%u'" class AliasesMap(MapFile): filename = 'sql-aliases.cf' mysql = "(SELECT concat(mb.address, '@', dom.name) FROM admin_mailbox mb INNER JOIN admin_domain dom ON mb.domain_id=dom.id WHERE mb.id IN (SELECT al_mb.mailbox_id FROM admin_alias_mboxes al_mb INNER JOIN admin_alias al ON al_mb.alias_id=al.id INNER JOIN admin_domain dom ON al.domain_id=dom.id WHERE dom.name='%d' AND dom.enabled=1 AND al.address='%u' AND al.enabled=1)) UNION (SELECT concat(al.address, '@', dom.name) FROM admin_alias al INNER JOIN admin_domain dom ON al.domain_id=dom.id WHERE al.id IN (SELECT al_al.to_alias_id FROM admin_alias_aliases al_al INNER JOIN admin_alias al ON al_al.from_alias_id=al.id INNER JOIN admin_domain dom ON al.domain_id=dom.id WHERE dom.name='%d' AND dom.enabled=1 AND al.address='%u' AND al.enabled=1)) UNION (SELECT al.extmboxes FROM admin_alias al INNER JOIN admin_domain dom ON al.domain_id=dom.id WHERE dom.name='%d' AND dom.enabled=1 AND al.address='%u' AND al.enabled=1 AND al.extmboxes<>'')" postgres = "(SELECT mb.address || '@' || dom.name FROM admin_mailbox mb INNER JOIN admin_domain dom ON mb.domain_id=dom.id WHERE mb.id IN (SELECT al_mb.mailbox_id FROM admin_alias_mboxes al_mb INNER JOIN admin_alias al ON al_mb.alias_id=al.id INNER JOIN admin_domain dom ON al.domain_id=dom.id WHERE dom.name='%d' AND dom.enabled AND al.address='%u' AND al.enabled)) UNION (SELECT al.address || '@' || dom.name FROM admin_alias al INNER JOIN admin_domain dom ON al.domain_id=dom.id WHERE al.id IN (SELECT al_al.to_alias_id FROM admin_alias_aliases al_al INNER JOIN admin_alias al ON al_al.from_alias_id=al.id INNER JOIN admin_domain dom ON al.domain_id=dom.id WHERE dom.name='%d' AND dom.enabled AND al.address='%u' AND al.enabled)) UNION (SELECT al.extmboxes FROM admin_alias al INNER JOIN admin_domain dom ON al.domain_id=dom.id WHERE dom.name='%d' AND dom.enabled AND al.address='%u' AND al.enabled AND al.extmboxes<>'')" + sqlite = "(SELECT (mb.address || '@' || dom.name) FROM admin_mailbox mb INNER JOIN admin_domain dom ON mb.domain_id=dom.id WHERE mb.id IN (SELECT al_mb.mailbox_id FROM admin_alias_mboxes al_mb INNER JOIN admin_alias al ON al_mb.alias_id=al.id INNER JOIN admin_domain dom ON al.domain_id=dom.id WHERE dom.name='%d' AND dom.enabled=1 AND al.address='%u' AND al.enabled=1)) UNION (SELECT (al.address || '@' || dom.name) FROM admin_alias al INNER JOIN admin_domain dom ON al.domain_id=dom.id WHERE al.id IN (SELECT al_al.to_alias_id FROM admin_alias_aliases al_al INNER JOIN admin_alias al ON al_al.from_alias_id=al.id INNER JOIN admin_domain dom ON al.domain_id=dom.id WHERE dom.name='%d' AND dom.enabled=1 AND al.address='%u' AND al.enabled=1)) UNION (SELECT al.extmboxes FROM admin_alias al INNER JOIN admin_domain dom ON al.domain_id=dom.id WHERE dom.name='%d' AND dom.enabled=1 AND al.address='%u' AND al.enabled=1 AND al.extmboxes<>'')" class DomainAliasesMailboxesMap(MapFile): filename = 'sql-domain-aliases-mailboxes.cf' mysql = "(SELECT concat(mb.address, '@', dom.name) FROM admin_domainalias domal INNER JOIN admin_domain dom ON domal.target_id=dom.id INNER JOIN admin_mailbox mb ON mb.domain_id=dom.id WHERE domal.name='%d' AND dom.enabled=1 AND mb.address='%u') UNION (SELECT concat(al.address, '@', dom.name) FROM admin_domainalias domal INNER JOIN admin_domain dom ON domal.target_id=dom.id INNER JOIN admin_alias al ON al.domain_id=dom.id WHERE domal.name='%d' AND dom.enabled=1 AND al.address='%u')" postgres = "(SELECT mb.address || '@' || dom.name FROM admin_domainalias domal INNER JOIN admin_domain dom ON domal.target_id=dom.id INNER JOIN admin_mailbox mb ON mb.domain_id=dom.id WHERE domal.name='%d' AND dom.enabled AND mb.address='%u') UNION (SELECT al.address || '@' || dom.name FROM admin_domainalias domal INNER JOIN admin_domain dom ON domal.target_id=dom.id INNER JOIN admin_alias al ON al.domain_id=dom.id WHERE domal.name='%d' AND dom.enabled AND al.address='%u')" + sqlite = "(SELECT mb.address || '@' || dom.name FROM admin_domainalias domal INNER JOIN admin_domain dom ON domal.target_id=dom.id INNER JOIN admin_mailbox mb ON mb.domain_id=dom.id WHERE domal.name='%d' AND dom.enabled=1 AND mb.address='%u') UNION (SELECT al.address || '@' || dom.name FROM admin_domainalias domal INNER JOIN admin_domain dom ON domal.target_id=dom.id INNER JOIN admin_alias al ON al.domain_id=dom.id WHERE domal.name='%d' AND dom.enabled=1 AND al.address='%u')" class CatchallAliasesMap(MapFile): filename = 'sql-catchall-aliases.cf' mysql = "(SELECT concat(mb.address, '@', dom.name) FROM admin_mailbox mb INNER JOIN admin_domain dom ON mb.domain_id=dom.id WHERE mb.id IN (SELECT al_mb.mailbox_id FROM admin_alias al INNER JOIN admin_domain dom ON al.domain_id=dom.id INNER JOIN admin_alias_mboxes al_mb ON al.id=al_mb.alias_id WHERE al.enabled=1 AND al.address='*' AND dom.name='%d' AND dom.enabled=1)) UNION (SELECT al.extmboxes FROM admin_alias al INNER JOIN admin_domain dom ON al.domain_id=dom.id WHERE al.enabled='1' AND al.extmboxes<>'' AND al.address='*' AND dom.name='%d' AND dom.enabled=1)" postgres = "(SELECT mb.address || '@' || dom.name FROM admin_mailbox mb INNER JOIN admin_domain dom ON mb.domain_id=dom.id WHERE mb.id IN (SELECT al_mb.mailbox_id FROM admin_alias al INNER JOIN admin_domain dom ON al.domain_id=dom.id INNER JOIN admin_alias_mboxes al_mb ON al.id=al_mb.alias_id WHERE al.enabled AND al.address='*' AND dom.name='%d' AND dom.enabled)) UNION (SELECT al.extmboxes FROM admin_alias al INNER JOIN admin_domain dom ON al.domain_id=dom.id WHERE al.enabled AND al.extmboxes<>'' AND al.address='*' AND dom.name='%d' AND dom.enabled)" + sqlite = "(SELECT (mb.address || '@' || dom.name) FROM admin_mailbox mb INNER JOIN admin_domain dom ON mb.domain_id=dom.id WHERE mb.id IN (SELECT al_mb.mailbox_id FROM admin_alias al INNER JOIN admin_domain dom ON al.domain_id=dom.id INNER JOIN admin_alias_mboxes al_mb ON al.id=al_mb.alias_id WHERE al.enabled=1 AND al.address='*' AND dom.name='%d' AND dom.enabled=1)) UNION (SELECT al.extmboxes FROM admin_alias al INNER JOIN admin_domain dom ON al.domain_id=dom.id WHERE al.enabled='1' AND al.extmboxes<>'' AND al.address='*' AND dom.name='%d' AND dom.enabled=1)" class MaintainMap(MapFile): filename = 'sql-maintain.cf' mysql = "SELECT '450 Requested mail action not taken: mailbox unavailable' FROM admin_mailbox mb INNER JOIN admin_domain dom ON mb.domain_id=dom.id INNER JOIN admin_mailboxoperation mbop ON mbop.mailbox_id=mb.id WHERE dom.name='%d' AND mb.address='%u' LIMIT 1" postgres = "SELECT '450 Requested mail action not taken: mailbox unavailable' FROM admin_mailbox mb INNER JOIN admin_domain dom ON mb.domain_id=dom.id INNER JOIN admin_mailboxoperation mbop ON mbop.mailbox_id=mb.id WHERE dom.name='%d' AND mb.address='%u' LIMIT 1" + sqlite = "SELECT '450 Requested mail action not taken: mailbox unavailable' FROM admin_mailbox mb INNER JOIN admin_domain dom ON mb.domain_id=dom.id INNER JOIN admin_mailboxoperation mbop ON mbop.mailbox_id=mb.id WHERE dom.name='%d' AND mb.address='%u' LIMIT 1" class TransportMap(MapFile): @@ -76,6 +118,7 @@ class TransportMap(MapFile): filename = 'sql-transport.cf' mysql = "SELECT method FROM postfix_autoreply_transport WHERE domain='%s'" postgres = "SELECT method FROM postfix_autoreply_transport WHERE domain='%s'" + sqlite = "SELECT method FROM postfix_autoreply_transport WHERE domain='%s'" class AutoRepliesMap(MapFile): @@ -83,6 +126,7 @@ class AutoRepliesMap(MapFile): filename = 'sql-autoreplies.cf' mysql = "SELECT full_address, autoreply_address FROM postfix_autoreply_alias WHERE full_address='%s'" postgres = "SELECT full_address, autoreply_address FROM postfix_autoreply_alias WHERE full_address='%s'" + sqlite = "SELECT full_address, autoreply_address FROM postfix_autoreply_alias WHERE full_address='%s'" class PostfixMapsCommand(Command): @@ -90,30 +134,23 @@ class PostfixMapsCommand(Command): def __init__(self, *args, **kwargs): super(PostfixMapsCommand, self).__init__(*args, **kwargs) - self._parser.add_argument('destdir', type=str, - help='Directory that will contain generated map files') - self._parser.add_argument('--dbtype', type=str, choices=['mysql', 'postgres'], default='mysql', - help='Used database type') - self._parser.add_argument('--dbhost', type=str, default='127.0.0.1', - help='Database host address') - self._parser.add_argument('--categories', type=str, nargs='*', default=['std'], - help='Map file categories to generate (choices: std, autoreply)') + self._parser.add_argument( + 'destdir', type=str, + help='Directory that will contain generated map files' + ) + self._parser.add_argument( + '--dbtype', type=str, choices=['mysql', 'postgres', 'sqlite'], + default='mysql', + help='Used database type' + ) + self._parser.add_argument( + '--categories', type=str, nargs='*', default=['std'], + help='Map file categories to generate (choices: std, autoreply)' + ) def handle(self, parsed_args): - dbname = raw_input('Database name: ') - dbuser = raw_input('Username: ') - dbpass = getpass.getpass('Password: ') - - try: - os.mkdir(parsed_args.destdir) - except OSError: - pass - - for k, v in globals().iteritems(): - if not inspect.isclass(v) or not issubclass(v, MapFile): - continue - if not v.category in parsed_args.categories: - continue - v().render(parsed_args.destdir, parsed_args.dbtype, - dbname=dbname, dbuser=dbuser, dbpassword=dbpass, - dbhost=parsed_args.dbhost) + if parsed_args.dbtype == 'sqlite': + g = SQLiteMapFilesGenerator() + else: + g = MapFilesGenerator() + g.render(parsed_args)