, 2014
+# Patrick Koetter , 2010
+msgid ""
+msgstr ""
+"Project-Id-Version: Modoboa\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-09-17 11:24+0200\n"
+"PO-Revision-Date: 2014-12-03 11:02+0000\n"
+"Last-Translator: Arvedui \n"
+"Language-Team: German (http://www.transifex.com/projects/p/modoboa/language/"
+"de/)\n"
+"Language: de\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: forms.py:20
+msgid "General"
+msgstr "Allgemein"
+
+#: forms.py:23
+msgid "Path to the log file"
+msgstr "Pfad zur Log-Datei"
+
+#: forms.py:25
+msgid "Path to log file used to collect statistics"
+msgstr "Pfad zur Log-Datei, um Statistiken zu sammeln"
+
+#: forms.py:30
+msgid "Directory to store RRD files"
+msgstr "Ablageverzeichnis für RRD-Dateien"
+
+#: forms.py:33
+msgid "Path to directory where RRD files are stored"
+msgstr "Pfad zum Verzeichnis in dem die RRD-Dateien abgelegt werden"
+
+#: forms.py:38
+#, fuzzy
+#| msgid "rejected messages"
+msgid "Show greylisted messages"
+msgstr "Abgewiesene Nachrichten"
+
+#: forms.py:41
+msgid "Differentiate between hard and soft rejects (greylisting)"
+msgstr ""
+
+#: forms.py:59 handlers.py:22
+msgid "Statistics"
+msgstr "Statistiken"
+
+#: graphics.py:95
+msgid "Failed to locate rrdtool binary."
+msgstr ""
+
+#: graphics.py:174
+msgid "Average traffic (msgs/min)"
+msgstr "Durchschnittlicher Traffic (Nachrichten/min)"
+
+#: graphics.py:177
+msgid "sent messages"
+msgstr "Gesendete Nachrichten"
+
+#: graphics.py:178
+msgid "received messages"
+msgstr "Empfangene Nachrichten"
+
+#: graphics.py:179
+msgid "bounced messages"
+msgstr "Zurückgeworfene Nachrichten"
+
+#: graphics.py:180
+msgid "rejected messages"
+msgstr "Abgewiesene Nachrichten"
+
+#: graphics.py:181
+#, fuzzy
+#| msgid "sent messages"
+msgid "virus messages"
+msgstr "Gesendete Nachrichten"
+
+#: graphics.py:182
+#, fuzzy
+#| msgid "sent messages"
+msgid "spam messages"
+msgstr "Gesendete Nachrichten"
+
+#: graphics.py:189
+#, fuzzy
+#| msgid "rejected messages"
+msgid "greylisted messages"
+msgstr "Abgewiesene Nachrichten"
+
+#: graphics.py:200
+msgid "Average normal traffic size (bytes/min)"
+msgstr "Durchschnittliche Traffic-Menge (Bytes/min)"
+
+#: graphics.py:203
+msgid "received size"
+msgstr "Empfangsgröße"
+
+#: graphics.py:205
+msgid "sent size"
+msgstr "Versendegröße"
+
+#: graphics.py:213
+msgid "Mail traffic"
+msgstr "Mailverkehr"
+
+#: graphics.py:258
+msgid "Average account creation (account/hour)"
+msgstr ""
+
+#: graphics.py:261
+msgid "New accounts"
+msgstr ""
+
+#: graphics.py:268
+msgid "Accounts"
+msgstr ""
+
+#: templates/maillog/index.html:4
+msgid "Graphical statistics"
+msgstr "Grafische Statistiken"
+
+#: templates/maillog/index.html:44
+msgid "Search a domain"
+msgstr "Domain suchen"
+
+#: templates/maillog/index.html:77
+msgid "Apply"
+msgstr "Antworten"
+
+#: views.py:33
+msgid "No statistics available"
+msgstr "Keine Statistik-Daten verfügbar"
+
+#: views.py:40
+msgid "Day"
+msgstr "Tag"
+
+#: views.py:41
+msgid "Week"
+msgstr "Woche"
+
+#: views.py:42
+msgid "Month"
+msgstr "Monat"
+
+#: views.py:43
+msgid "Year"
+msgstr "Jahr"
+
+#: views.py:44
+msgid "Custom"
+msgstr "Benutzerdefiniert"
+
+#: views.py:63
+msgid "Unknown graphic set"
+msgstr "Unbekanntes graphisches Set"
+
+#: views.py:68
+#, fuzzy
+#| msgid "Unknown graphic set"
+msgid "Unknown domain"
+msgstr "Unbekanntes graphisches Set"
+
+#: views.py:73
+msgid "Bad custom period"
+msgstr "Ungültiger individueller Zeitraum"
+
+#~ msgid "Graphical statistics about emails traffic using RRDtool"
+#~ msgstr "Grafische E-Mail-Statistik (setzt RRDtool ein)"
diff --git a/modoboa/maillog/locale/de/LC_MESSAGES/djangojs.po b/modoboa/maillog/locale/de/LC_MESSAGES/djangojs.po
new file mode 100644
index 000000000..2be39c4bc
--- /dev/null
+++ b/modoboa/maillog/locale/de/LC_MESSAGES/djangojs.po
@@ -0,0 +1,25 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Translators:
+# Arvedui , 2014
+# Patrick Koetter , 2010
+msgid ""
+msgstr ""
+"Project-Id-Version: Modoboa\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2017-03-03 09:37+0100\n"
+"PO-Revision-Date: 2014-12-01 12:12+0000\n"
+"Last-Translator: Arvedui \n"
+"Language-Team: German (http://www.transifex.com/projects/p/modoboa/language/"
+"de/)\n"
+"Language: de\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: static/modoboa_stats/js/stats.js:43
+msgid "Search a domain"
+msgstr "Domain suchen"
diff --git a/modoboa/maillog/locale/el_GR/LC_MESSAGES/django.po b/modoboa/maillog/locale/el_GR/LC_MESSAGES/django.po
new file mode 100644
index 000000000..a493fd517
--- /dev/null
+++ b/modoboa/maillog/locale/el_GR/LC_MESSAGES/django.po
@@ -0,0 +1,176 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Translators:
+# Giannis Kapetanakis , 2016
+# Kostas Moumouris , 2017
+msgid ""
+msgstr ""
+"Project-Id-Version: Modoboa\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-09-17 11:24+0200\n"
+"PO-Revision-Date: 2017-10-12 07:37+0000\n"
+"Last-Translator: Kostas Moumouris \n"
+"Language-Team: Greek (Greece) (http://www.transifex.com/tonio/modoboa/"
+"language/el_GR/)\n"
+"Language: el_GR\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: forms.py:20
+msgid "General"
+msgstr "Γενικά"
+
+#: forms.py:23
+msgid "Path to the log file"
+msgstr "Διαδρομή προς το αρχείο καταγραφής"
+
+#: forms.py:25
+msgid "Path to log file used to collect statistics"
+msgstr "Διαδρομή προς το αρχείο καταγραφής για την συλλογή στατιστικών"
+
+#: forms.py:30
+msgid "Directory to store RRD files"
+msgstr "Κατάλογος αρχείων για την αποθήκευση των αρχείων RRD"
+
+#: forms.py:33
+msgid "Path to directory where RRD files are stored"
+msgstr "Διαδρομή προς τον κατάλογο αρχείων για την αποθήκευση αρχείων RRD"
+
+#: forms.py:38
+msgid "Show greylisted messages"
+msgstr "Εμφάνιση μηνυμάτων greylisted"
+
+#: forms.py:41
+msgid "Differentiate between hard and soft rejects (greylisting)"
+msgstr ""
+"Διαφοροποίηση μεταξύ σκληρών (hard) και μαλακών (soft) απορρίψεων "
+"(greylisting)"
+
+#: forms.py:59 handlers.py:22
+msgid "Statistics"
+msgstr "Στατιστικά"
+
+#: graphics.py:95
+msgid "Failed to locate rrdtool binary."
+msgstr "Αδυναμία εύρεσης του εκτελέσουμε rrdtool."
+
+#: graphics.py:174
+msgid "Average traffic (msgs/min)"
+msgstr "Μέσος όρος κίνησης (μηνύματα/λεπτό)"
+
+#: graphics.py:177
+msgid "sent messages"
+msgstr "απεσταλμένα μηνύματα"
+
+#: graphics.py:178
+msgid "received messages"
+msgstr "ληφθέντα μηνήματα"
+
+#: graphics.py:179
+msgid "bounced messages"
+msgstr "Μηνύματα bounced "
+
+#: graphics.py:180
+msgid "rejected messages"
+msgstr "απορριφθέντα μηνύματα"
+
+#: graphics.py:181
+msgid "virus messages"
+msgstr ""
+"Μηνύματα με ιούς \n"
+" "
+
+#: graphics.py:182
+msgid "spam messages"
+msgstr "Διαφημιστικά μηνύματα"
+
+#: graphics.py:189
+msgid "greylisted messages"
+msgstr ""
+"Μηνύματα που είναι greylisted\n"
+" "
+
+#: graphics.py:200
+msgid "Average normal traffic size (bytes/min)"
+msgstr "Μέσος όρος φυσιολογικού traffic (bytes/min) "
+
+#: graphics.py:203
+msgid "received size"
+msgstr "Μέγεθος λαμβανόμενου"
+
+#: graphics.py:205
+msgid "sent size"
+msgstr "μέγεθος αποστολής"
+
+#: graphics.py:213
+msgid "Mail traffic"
+msgstr "Κίνηση Mail"
+
+#: graphics.py:258
+msgid "Average account creation (account/hour)"
+msgstr "Μέσος όρος δημιουργίας λογαριασμών (λογαριασμοί/ώρα)"
+
+#: graphics.py:261
+msgid "New accounts"
+msgstr "Νέοι λογαριασμοί"
+
+#: graphics.py:268
+msgid "Accounts"
+msgstr "Λογαριασμοί"
+
+#: templates/maillog/index.html:4
+msgid "Graphical statistics"
+msgstr "Γραφικά στατιστικά"
+
+#: templates/maillog/index.html:44
+msgid "Search a domain"
+msgstr "Αναζήτηση ενός domain"
+
+#: templates/maillog/index.html:77
+msgid "Apply"
+msgstr "Εφαρμογή"
+
+#: views.py:33
+msgid "No statistics available"
+msgstr "Δεν υπάρχουν διαθέσιμα στατιστικά"
+
+#: views.py:40
+msgid "Day"
+msgstr "Ημέρα"
+
+#: views.py:41
+msgid "Week"
+msgstr "Εβδομάδα"
+
+#: views.py:42
+msgid "Month"
+msgstr "Μήνας"
+
+#: views.py:43
+msgid "Year"
+msgstr "Έτος"
+
+#: views.py:44
+msgid "Custom"
+msgstr "Προσαρμοσμένο"
+
+#: views.py:63
+msgid "Unknown graphic set"
+msgstr "Άγνωστο σετ γραφικών"
+
+#: views.py:68
+#, fuzzy
+#| msgid "Unknown graphic set"
+msgid "Unknown domain"
+msgstr "Άγνωστο σετ γραφικών"
+
+#: views.py:73
+msgid "Bad custom period"
+msgstr "Λάθος προσαρμοσμένη περίοδος"
+
+#~ msgid "Graphical statistics about emails traffic using RRDtool"
+#~ msgstr "Γραφικά στατιστικά της κίνησης email με RRDtool"
diff --git a/modoboa/maillog/locale/el_GR/LC_MESSAGES/djangojs.po b/modoboa/maillog/locale/el_GR/LC_MESSAGES/djangojs.po
new file mode 100644
index 000000000..cf1b82a5f
--- /dev/null
+++ b/modoboa/maillog/locale/el_GR/LC_MESSAGES/djangojs.po
@@ -0,0 +1,23 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Translators:
+# Giannis Kapetanakis , 2016
+msgid ""
+msgstr ""
+"Project-Id-Version: Modoboa\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2017-03-03 09:37+0100\n"
+"PO-Revision-Date: 2017-09-22 22:21+0000\n"
+"Last-Translator: Giannis Kapetanakis \n"
+"Language-Team: Greek (Greece) (http://www.transifex.com/tonio/modoboa/language/el_GR/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: el_GR\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: static/modoboa_stats/js/stats.js:43
+msgid "Search a domain"
+msgstr "Αναζήτηση ενός domain"
diff --git a/modoboa/maillog/locale/en/LC_MESSAGES/django.po b/modoboa/maillog/locale/en/LC_MESSAGES/django.po
new file mode 100644
index 000000000..0b5684fc4
--- /dev/null
+++ b/modoboa/maillog/locale/en/LC_MESSAGES/django.po
@@ -0,0 +1,162 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR , YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-09-17 11:24+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME \n"
+"Language-Team: LANGUAGE \n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: forms.py:20
+msgid "General"
+msgstr ""
+
+#: forms.py:23
+msgid "Path to the log file"
+msgstr ""
+
+#: forms.py:25
+msgid "Path to log file used to collect statistics"
+msgstr ""
+
+#: forms.py:30
+msgid "Directory to store RRD files"
+msgstr ""
+
+#: forms.py:33
+msgid "Path to directory where RRD files are stored"
+msgstr ""
+
+#: forms.py:38
+msgid "Show greylisted messages"
+msgstr ""
+
+#: forms.py:41
+msgid "Differentiate between hard and soft rejects (greylisting)"
+msgstr ""
+
+#: forms.py:59 handlers.py:22
+msgid "Statistics"
+msgstr ""
+
+#: graphics.py:95
+msgid "Failed to locate rrdtool binary."
+msgstr ""
+
+#: graphics.py:174
+msgid "Average traffic (msgs/min)"
+msgstr ""
+
+#: graphics.py:177
+msgid "sent messages"
+msgstr ""
+
+#: graphics.py:178
+msgid "received messages"
+msgstr ""
+
+#: graphics.py:179
+msgid "bounced messages"
+msgstr ""
+
+#: graphics.py:180
+msgid "rejected messages"
+msgstr ""
+
+#: graphics.py:181
+msgid "virus messages"
+msgstr ""
+
+#: graphics.py:182
+msgid "spam messages"
+msgstr ""
+
+#: graphics.py:189
+msgid "greylisted messages"
+msgstr ""
+
+#: graphics.py:200
+msgid "Average normal traffic size (bytes/min)"
+msgstr ""
+
+#: graphics.py:203
+msgid "received size"
+msgstr ""
+
+#: graphics.py:205
+msgid "sent size"
+msgstr ""
+
+#: graphics.py:213
+msgid "Mail traffic"
+msgstr ""
+
+#: graphics.py:258
+msgid "Average account creation (account/hour)"
+msgstr ""
+
+#: graphics.py:261
+msgid "New accounts"
+msgstr ""
+
+#: graphics.py:268
+msgid "Accounts"
+msgstr ""
+
+#: templates/maillog/index.html:4
+msgid "Graphical statistics"
+msgstr ""
+
+#: templates/maillog/index.html:44
+msgid "Search a domain"
+msgstr ""
+
+#: templates/maillog/index.html:77
+msgid "Apply"
+msgstr ""
+
+#: views.py:33
+msgid "No statistics available"
+msgstr ""
+
+#: views.py:40
+msgid "Day"
+msgstr ""
+
+#: views.py:41
+msgid "Week"
+msgstr ""
+
+#: views.py:42
+msgid "Month"
+msgstr ""
+
+#: views.py:43
+msgid "Year"
+msgstr ""
+
+#: views.py:44
+msgid "Custom"
+msgstr ""
+
+#: views.py:63
+msgid "Unknown graphic set"
+msgstr ""
+
+#: views.py:68
+msgid "Unknown domain"
+msgstr ""
+
+#: views.py:73
+msgid "Bad custom period"
+msgstr ""
diff --git a/modoboa/maillog/locale/en/LC_MESSAGES/djangojs.po b/modoboa/maillog/locale/en/LC_MESSAGES/djangojs.po
new file mode 100644
index 000000000..11897ec58
--- /dev/null
+++ b/modoboa/maillog/locale/en/LC_MESSAGES/djangojs.po
@@ -0,0 +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.
+# FIRST AUTHOR , YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2017-03-03 09:37+0100\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME \n"
+"Language-Team: LANGUAGE \n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: static/modoboa_stats/js/stats.js:43
+msgid "Search a domain"
+msgstr ""
diff --git a/modoboa/maillog/locale/es/LC_MESSAGES/django.po b/modoboa/maillog/locale/es/LC_MESSAGES/django.po
new file mode 100644
index 000000000..09e528429
--- /dev/null
+++ b/modoboa/maillog/locale/es/LC_MESSAGES/django.po
@@ -0,0 +1,171 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Translators:
+# Carlos L , 2017
+# Evilham , 2017
+# Pedro Gracia , 2016
+msgid ""
+msgstr ""
+"Project-Id-Version: Modoboa\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-09-17 11:24+0200\n"
+"PO-Revision-Date: 2017-11-23 16:41+0000\n"
+"Last-Translator: Evilham \n"
+"Language-Team: Spanish (http://www.transifex.com/tonio/modoboa/language/"
+"es/)\n"
+"Language: es\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: forms.py:20
+msgid "General"
+msgstr "General"
+
+#: forms.py:23
+msgid "Path to the log file"
+msgstr "Ruta del fichero de log"
+
+#: forms.py:25
+msgid "Path to log file used to collect statistics"
+msgstr "Ruta al fichero de log usado para recolectar estadísticas"
+
+#: forms.py:30
+msgid "Directory to store RRD files"
+msgstr "Directorio para guardar los ficheros RRD"
+
+#: forms.py:33
+msgid "Path to directory where RRD files are stored"
+msgstr "Ruta al directorio dónde están almacenados los ficheros RRD"
+
+#: forms.py:38
+msgid "Show greylisted messages"
+msgstr ""
+
+#: forms.py:41
+msgid "Differentiate between hard and soft rejects (greylisting)"
+msgstr ""
+
+#: forms.py:59 handlers.py:22
+msgid "Statistics"
+msgstr "Estadísticas"
+
+#: graphics.py:95
+msgid "Failed to locate rrdtool binary."
+msgstr "Fallo al localizar el binario rrdtool"
+
+#: graphics.py:174
+msgid "Average traffic (msgs/min)"
+msgstr "Media del tráfico (msgs/min)"
+
+#: graphics.py:177
+msgid "sent messages"
+msgstr "mensajes enviados"
+
+#: graphics.py:178
+msgid "received messages"
+msgstr "mensajes recibidos"
+
+#: graphics.py:179
+msgid "bounced messages"
+msgstr "mensajes rebotados"
+
+#: graphics.py:180
+msgid "rejected messages"
+msgstr "mensajes bloqueados"
+
+#: graphics.py:181
+msgid "virus messages"
+msgstr "mensajes con virus"
+
+#: graphics.py:182
+msgid "spam messages"
+msgstr "Mensajes Basura"
+
+#: graphics.py:189
+msgid "greylisted messages"
+msgstr ""
+
+#: graphics.py:200
+msgid "Average normal traffic size (bytes/min)"
+msgstr "Media normal del tamaño del tráfico (bytes/min)"
+
+#: graphics.py:203
+msgid "received size"
+msgstr "tamaño recibido"
+
+#: graphics.py:205
+msgid "sent size"
+msgstr "tamaño enviado"
+
+#: graphics.py:213
+msgid "Mail traffic"
+msgstr "Tráfico de correo"
+
+#: graphics.py:258
+msgid "Average account creation (account/hour)"
+msgstr "Creación promedio de cuentas (cuentas/hora)"
+
+#: graphics.py:261
+msgid "New accounts"
+msgstr "Cuentas nuevos"
+
+#: graphics.py:268
+msgid "Accounts"
+msgstr "Cuentas"
+
+#: templates/maillog/index.html:4
+msgid "Graphical statistics"
+msgstr "Estadísticas gráficas"
+
+#: templates/maillog/index.html:44
+msgid "Search a domain"
+msgstr "Buscar un dominio"
+
+#: templates/maillog/index.html:77
+msgid "Apply"
+msgstr "Aplicar"
+
+#: views.py:33
+msgid "No statistics available"
+msgstr "No hay estadísticas disponibles"
+
+#: views.py:40
+msgid "Day"
+msgstr "Día"
+
+#: views.py:41
+msgid "Week"
+msgstr "Semana"
+
+#: views.py:42
+msgid "Month"
+msgstr "Mes"
+
+#: views.py:43
+msgid "Year"
+msgstr "Año"
+
+#: views.py:44
+msgid "Custom"
+msgstr "A medida"
+
+#: views.py:63
+msgid "Unknown graphic set"
+msgstr "Conjunto gráfico desconocido"
+
+#: views.py:68
+#, fuzzy
+#| msgid "Unknown graphic set"
+msgid "Unknown domain"
+msgstr "Conjunto gráfico desconocido"
+
+#: views.py:73
+msgid "Bad custom period"
+msgstr "Período elegido erróneo"
+
+#~ msgid "Graphical statistics about emails traffic using RRDtool"
+#~ msgstr "Estadísticas gráficas del tráfico de emails usando RRDtool"
diff --git a/modoboa/maillog/locale/es/LC_MESSAGES/djangojs.po b/modoboa/maillog/locale/es/LC_MESSAGES/djangojs.po
new file mode 100644
index 000000000..be3dec69f
--- /dev/null
+++ b/modoboa/maillog/locale/es/LC_MESSAGES/djangojs.po
@@ -0,0 +1,24 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Translators:
+# Pedro Gracia , 2011,2013
+# Pedro Gracia , 2010-2011
+msgid ""
+msgstr ""
+"Project-Id-Version: Modoboa\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2017-03-03 09:37+0100\n"
+"PO-Revision-Date: 2017-09-22 09:57+0000\n"
+"Last-Translator: Antoine Nguyen \n"
+"Language-Team: Spanish (http://www.transifex.com/tonio/modoboa/language/es/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: es\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: static/modoboa_stats/js/stats.js:43
+msgid "Search a domain"
+msgstr "Buscar un dominio"
diff --git a/modoboa/maillog/locale/fr/LC_MESSAGES/django.po b/modoboa/maillog/locale/fr/LC_MESSAGES/django.po
new file mode 100644
index 000000000..f88f00c33
--- /dev/null
+++ b/modoboa/maillog/locale/fr/LC_MESSAGES/django.po
@@ -0,0 +1,170 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Translators:
+# Antoine Nguyen , 2017
+msgid ""
+msgstr ""
+"Project-Id-Version: Modoboa\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-09-17 11:24+0200\n"
+"PO-Revision-Date: 2017-08-09 08:17+0000\n"
+"Last-Translator: Antoine Nguyen \n"
+"Language-Team: French (http://www.transifex.com/tonio/modoboa/language/fr/)\n"
+"Language: fr\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+
+#: forms.py:20
+msgid "General"
+msgstr "Général"
+
+#: forms.py:23
+msgid "Path to the log file"
+msgstr "Chemin d'accès vers le journal"
+
+#: forms.py:25
+msgid "Path to log file used to collect statistics"
+msgstr "Emplacement du log utilisé pour collecter les statistiques"
+
+#: forms.py:30
+msgid "Directory to store RRD files"
+msgstr "Répertoire où stocker les fichiers RRD"
+
+#: forms.py:33
+msgid "Path to directory where RRD files are stored"
+msgstr "Emplacement du répertoire de stockage des fichiers RRD"
+
+#: forms.py:38
+msgid "Show greylisted messages"
+msgstr "Afficher les messages greylistés"
+
+#: forms.py:41
+msgid "Differentiate between hard and soft rejects (greylisting)"
+msgstr "Différencie les rejets en douceur des rejets durs (greylisting)"
+
+#: forms.py:59 handlers.py:22
+msgid "Statistics"
+msgstr "Statistiques"
+
+#: graphics.py:95
+msgid "Failed to locate rrdtool binary."
+msgstr "Impossible de trouver le binaire rrdtool"
+
+#: graphics.py:174
+msgid "Average traffic (msgs/min)"
+msgstr "Trafic moyen (msgs/min)"
+
+#: graphics.py:177
+msgid "sent messages"
+msgstr "messages envoyés"
+
+#: graphics.py:178
+msgid "received messages"
+msgstr "messages reçus"
+
+#: graphics.py:179
+msgid "bounced messages"
+msgstr "messages non remis"
+
+#: graphics.py:180
+msgid "rejected messages"
+msgstr "messages rejetés"
+
+#: graphics.py:181
+msgid "virus messages"
+msgstr "messages de type virus"
+
+#: graphics.py:182
+msgid "spam messages"
+msgstr "messages de type spam"
+
+#: graphics.py:189
+msgid "greylisted messages"
+msgstr "Messages greylistés"
+
+#: graphics.py:200
+msgid "Average normal traffic size (bytes/min)"
+msgstr "Taille moyenne du trafic normal (octets/min)"
+
+#: graphics.py:203
+msgid "received size"
+msgstr "taille reçue"
+
+#: graphics.py:205
+msgid "sent size"
+msgstr "taille envoyée"
+
+#: graphics.py:213
+msgid "Mail traffic"
+msgstr "Trafic messagerie"
+
+#: graphics.py:258
+msgid "Average account creation (account/hour)"
+msgstr "Moyenne des créations de compte (compte/heure)"
+
+#: graphics.py:261
+msgid "New accounts"
+msgstr "Nouveaux comptes"
+
+#: graphics.py:268
+msgid "Accounts"
+msgstr "Comptes"
+
+#: templates/maillog/index.html:4
+msgid "Graphical statistics"
+msgstr "Statistiques graphiques"
+
+#: templates/maillog/index.html:44
+msgid "Search a domain"
+msgstr "Chercher un domaine"
+
+#: templates/maillog/index.html:77
+msgid "Apply"
+msgstr "Appliquer"
+
+#: views.py:33
+msgid "No statistics available"
+msgstr "Aucune statistique disponible"
+
+#: views.py:40
+msgid "Day"
+msgstr "Jour"
+
+#: views.py:41
+msgid "Week"
+msgstr "Semaine"
+
+#: views.py:42
+msgid "Month"
+msgstr "Mois"
+
+#: views.py:43
+msgid "Year"
+msgstr "Année"
+
+#: views.py:44
+msgid "Custom"
+msgstr "Personnalisée"
+
+#: views.py:63
+msgid "Unknown graphic set"
+msgstr "Jeu de graphiques inconnu"
+
+#: views.py:68
+#, fuzzy
+#| msgid "Unknown graphic set"
+msgid "Unknown domain"
+msgstr "Jeu de graphiques inconnu"
+
+#: views.py:73
+msgid "Bad custom period"
+msgstr "Mauvaise période personnalisée"
+
+#~ msgid "Graphical statistics about emails traffic using RRDtool"
+#~ msgstr ""
+#~ "Statistiques graphiques concernant le trafic des messages utilisant "
+#~ "RRDtool"
diff --git a/modoboa/maillog/locale/fr/LC_MESSAGES/djangojs.po b/modoboa/maillog/locale/fr/LC_MESSAGES/djangojs.po
new file mode 100644
index 000000000..c375f231b
--- /dev/null
+++ b/modoboa/maillog/locale/fr/LC_MESSAGES/djangojs.po
@@ -0,0 +1,22 @@
+# Modoboa french translation
+# Copyright (C) 2009-2013
+# This file is distributed under the same license as the Modoboa package.
+# Antoine Nguyen , 2009-2013.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: modoboa-1.0.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2017-03-03 09:37+0100\n"
+"PO-Revision-Date: 2011-01-07 10:00+0100\n"
+"Last-Translator: Antoine Nguyen \n"
+"Language-Team: FR \n"
+"Language: Français\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: static/modoboa_stats/js/stats.js:43
+msgid "Search a domain"
+msgstr "Checher un domaine"
diff --git a/modoboa/maillog/locale/it/LC_MESSAGES/django.po b/modoboa/maillog/locale/it/LC_MESSAGES/django.po
new file mode 100644
index 000000000..402230507
--- /dev/null
+++ b/modoboa/maillog/locale/it/LC_MESSAGES/django.po
@@ -0,0 +1,177 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# Giancarlo Gaifas , 2013.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-09-17 11:24+0200\n"
+"PO-Revision-Date: 2013-12-23 00:56+0100\n"
+"Last-Translator: Alberto Berti \n"
+"Language-Team: ITALIANO \n"
+"Language: it\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: forms.py:20
+msgid "General"
+msgstr "Generale"
+
+#: forms.py:23
+msgid "Path to the log file"
+msgstr "Percorso del file di log"
+
+#: forms.py:25
+msgid "Path to log file used to collect statistics"
+msgstr "Percorso del file di log utilizzato per raccogliere le statistiche"
+
+#: forms.py:30
+msgid "Directory to store RRD files"
+msgstr "Cartella dei file RRD"
+
+#: forms.py:33
+msgid "Path to directory where RRD files are stored"
+msgstr "Percorso della cartella dove sono memorizzati i file RRD"
+
+#: forms.py:38
+#, fuzzy
+#| msgid "rejected messages"
+msgid "Show greylisted messages"
+msgstr "messaggi respinti"
+
+#: forms.py:41
+msgid "Differentiate between hard and soft rejects (greylisting)"
+msgstr ""
+
+#: forms.py:59 handlers.py:22
+msgid "Statistics"
+msgstr "Statistiche"
+
+#: graphics.py:95
+msgid "Failed to locate rrdtool binary."
+msgstr ""
+
+#: graphics.py:174
+#, fuzzy
+msgid "Average traffic (msgs/min)"
+msgstr "Dimensione del traffico normale medio"
+
+#: graphics.py:177
+msgid "sent messages"
+msgstr "messaggi inviati"
+
+#: graphics.py:178
+msgid "received messages"
+msgstr "messaggi ricevuti"
+
+#: graphics.py:179
+msgid "bounced messages"
+msgstr "messaggi di rimbalzo"
+
+#: graphics.py:180
+msgid "rejected messages"
+msgstr "messaggi respinti"
+
+#: graphics.py:181
+#, fuzzy
+#| msgid "sent messages"
+msgid "virus messages"
+msgstr "messaggi inviati"
+
+#: graphics.py:182
+#, fuzzy
+#| msgid "sent messages"
+msgid "spam messages"
+msgstr "messaggi inviati"
+
+#: graphics.py:189
+#, fuzzy
+#| msgid "rejected messages"
+msgid "greylisted messages"
+msgstr "messaggi respinti"
+
+#: graphics.py:200
+#, fuzzy
+msgid "Average normal traffic size (bytes/min)"
+msgstr "Dimensione del traffico normale medio"
+
+#: graphics.py:203
+msgid "received size"
+msgstr "dimensione di ricezione"
+
+#: graphics.py:205
+msgid "sent size"
+msgstr "dimensione di spedizione"
+
+#: graphics.py:213
+msgid "Mail traffic"
+msgstr "Traffico mail"
+
+#: graphics.py:258
+msgid "Average account creation (account/hour)"
+msgstr ""
+
+#: graphics.py:261
+msgid "New accounts"
+msgstr ""
+
+#: graphics.py:268
+msgid "Accounts"
+msgstr ""
+
+#: templates/maillog/index.html:4
+msgid "Graphical statistics"
+msgstr "Grafico statistiche"
+
+#: templates/maillog/index.html:44
+msgid "Search a domain"
+msgstr "Ricerca un dominio"
+
+#: templates/maillog/index.html:77
+msgid "Apply"
+msgstr "Applica"
+
+#: views.py:33
+msgid "No statistics available"
+msgstr "Non sono disponibili statistiche"
+
+#: views.py:40
+msgid "Day"
+msgstr "Giorno"
+
+#: views.py:41
+msgid "Week"
+msgstr "Settimana"
+
+#: views.py:42
+msgid "Month"
+msgstr "Mese"
+
+#: views.py:43
+msgid "Year"
+msgstr "Anno"
+
+#: views.py:44
+msgid "Custom"
+msgstr ""
+
+#: views.py:63
+msgid "Unknown graphic set"
+msgstr "Impostazione grafica sconosciuta"
+
+#: views.py:68
+#, fuzzy
+#| msgid "Unknown graphic set"
+msgid "Unknown domain"
+msgstr "Impostazione grafica sconosciuta"
+
+#: views.py:73
+msgid "Bad custom period"
+msgstr "Periodo personalizzato sbagliato"
+
+#~ msgid "Graphical statistics about emails traffic using RRDtool"
+#~ msgstr "Grafico delle statistiche sul traffico e-mail utilizzando RRDtool"
diff --git a/modoboa/maillog/locale/it/LC_MESSAGES/djangojs.po b/modoboa/maillog/locale/it/LC_MESSAGES/djangojs.po
new file mode 100644
index 000000000..220984a84
--- /dev/null
+++ b/modoboa/maillog/locale/it/LC_MESSAGES/djangojs.po
@@ -0,0 +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.
+# FIRST AUTHOR , YEAR.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Modoboa\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2017-03-03 09:37+0100\n"
+"PO-Revision-Date: 2013-12-23 00:58+0100\n"
+"Last-Translator: Alberto Berti \n"
+"Language-Team: Italiano \n"
+"Language: it\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: static/modoboa_stats/js/stats.js:43
+msgid "Search a domain"
+msgstr "Cerca un dominio"
diff --git a/modoboa/maillog/locale/ja_JP/LC_MESSAGES/django.po b/modoboa/maillog/locale/ja_JP/LC_MESSAGES/django.po
new file mode 100644
index 000000000..1b8b19545
--- /dev/null
+++ b/modoboa/maillog/locale/ja_JP/LC_MESSAGES/django.po
@@ -0,0 +1,179 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Translators:
+# cwatanab , 2016
+msgid ""
+msgstr ""
+"Project-Id-Version: Modoboa\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-09-17 11:24+0200\n"
+"PO-Revision-Date: 2016-06-07 08:56+0000\n"
+"Last-Translator: cwatanab \n"
+"Language-Team: Japanese (Japan) (http://www.transifex.com/tonio/modoboa/"
+"language/ja_JP/)\n"
+"Language: ja_JP\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+
+#: forms.py:20
+msgid "General"
+msgstr "全般"
+
+#: forms.py:23
+msgid "Path to the log file"
+msgstr "ログファイルへのパス"
+
+#: forms.py:25
+msgid "Path to log file used to collect statistics"
+msgstr "統計情報を収集するために使用されたログファイルへのパス"
+
+#: forms.py:30
+msgid "Directory to store RRD files"
+msgstr "RRD ファイルを保管するためのディレクトリ"
+
+#: forms.py:33
+msgid "Path to directory where RRD files are stored"
+msgstr "RRD ファイルが保管されたディレクトリへのパス"
+
+#: forms.py:38
+#, fuzzy
+#| msgid "rejected messages"
+msgid "Show greylisted messages"
+msgstr "拒否メッセージ"
+
+#: forms.py:41
+msgid "Differentiate between hard and soft rejects (greylisting)"
+msgstr ""
+
+#: forms.py:59 handlers.py:22
+msgid "Statistics"
+msgstr "統計情報"
+
+#: graphics.py:95
+msgid "Failed to locate rrdtool binary."
+msgstr ""
+
+#: graphics.py:174
+msgid "Average traffic (msgs/min)"
+msgstr "平均トラフィック (通/分)"
+
+#: graphics.py:177
+msgid "sent messages"
+msgstr "送信メッセージ"
+
+#: graphics.py:178
+msgid "received messages"
+msgstr "受信メッセージ"
+
+#: graphics.py:179
+msgid "bounced messages"
+msgstr "バウンスメッセージ"
+
+#: graphics.py:180
+msgid "rejected messages"
+msgstr "拒否メッセージ"
+
+#: graphics.py:181
+#, fuzzy
+#| msgid "sent messages"
+msgid "virus messages"
+msgstr "送信メッセージ"
+
+#: graphics.py:182
+#, fuzzy
+#| msgid "sent messages"
+msgid "spam messages"
+msgstr "送信メッセージ"
+
+#: graphics.py:189
+#, fuzzy
+#| msgid "rejected messages"
+msgid "greylisted messages"
+msgstr "拒否メッセージ"
+
+#: graphics.py:200
+msgid "Average normal traffic size (bytes/min)"
+msgstr "平均トラフィックサイズ (バイト/分)"
+
+#: graphics.py:203
+msgid "received size"
+msgstr "受信サイズ"
+
+#: graphics.py:205
+msgid "sent size"
+msgstr "送信サイズ"
+
+#: graphics.py:213
+msgid "Mail traffic"
+msgstr "メールトラフィック"
+
+#: graphics.py:258
+msgid "Average account creation (account/hour)"
+msgstr ""
+
+#: graphics.py:261
+msgid "New accounts"
+msgstr ""
+
+#: graphics.py:268
+msgid "Accounts"
+msgstr ""
+
+#: templates/maillog/index.html:4
+msgid "Graphical statistics"
+msgstr "グラフィカルな統計情報"
+
+#: templates/maillog/index.html:44
+msgid "Search a domain"
+msgstr "ドメインを検索"
+
+#: templates/maillog/index.html:77
+msgid "Apply"
+msgstr "適用"
+
+#: views.py:33
+msgid "No statistics available"
+msgstr "統計情報がありません"
+
+#: views.py:40
+msgid "Day"
+msgstr "日"
+
+#: views.py:41
+msgid "Week"
+msgstr "週"
+
+#: views.py:42
+msgid "Month"
+msgstr "月"
+
+#: views.py:43
+msgid "Year"
+msgstr "年"
+
+#: views.py:44
+msgid "Custom"
+msgstr "カスタム"
+
+#: views.py:63
+msgid "Unknown graphic set"
+msgstr "不明なグラフィックセット"
+
+#: views.py:68
+#, fuzzy
+#| msgid "Unknown graphic set"
+msgid "Unknown domain"
+msgstr "不明なグラフィックセット"
+
+#: views.py:73
+msgid "Bad custom period"
+msgstr "カスタム期間が不正です"
+
+#~ msgid "Graphical statistics about emails traffic using RRDtool"
+#~ msgstr ""
+#~ "RRDtool を使用して、電子メールトラフィックに関するグラフィカルな統計情報を"
+#~ "表示します。"
diff --git a/modoboa/maillog/locale/ja_JP/LC_MESSAGES/djangojs.po b/modoboa/maillog/locale/ja_JP/LC_MESSAGES/djangojs.po
new file mode 100644
index 000000000..80202fcf6
--- /dev/null
+++ b/modoboa/maillog/locale/ja_JP/LC_MESSAGES/djangojs.po
@@ -0,0 +1,24 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Translators:
+# cwatanab , 2016
+msgid ""
+msgstr ""
+"Project-Id-Version: Modoboa\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2017-03-03 09:37+0100\n"
+"PO-Revision-Date: 2016-06-07 07:16+0000\n"
+"Last-Translator: cwatanab \n"
+"Language-Team: Japanese (Japan) (http://www.transifex.com/tonio/modoboa/"
+"language/ja_JP/)\n"
+"Language: ja_JP\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+
+#: static/modoboa_stats/js/stats.js:43
+msgid "Search a domain"
+msgstr "ドメインを検索"
diff --git a/modoboa/maillog/locale/nl_NL/LC_MESSAGES/django.po b/modoboa/maillog/locale/nl_NL/LC_MESSAGES/django.po
new file mode 100644
index 000000000..1f24ac57d
--- /dev/null
+++ b/modoboa/maillog/locale/nl_NL/LC_MESSAGES/django.po
@@ -0,0 +1,178 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Translators:
+# Andrew , 2014
+# TuxBrother , 2014
+msgid ""
+msgstr ""
+"Project-Id-Version: Modoboa\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-09-17 11:24+0200\n"
+"PO-Revision-Date: 2014-12-31 11:37+0000\n"
+"Last-Translator: TuxBrother \n"
+"Language-Team: Dutch (Netherlands) (http://www.transifex.com/projects/p/"
+"modoboa/language/nl_NL/)\n"
+"Language: nl_NL\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: forms.py:20
+msgid "General"
+msgstr "Algemeen"
+
+#: forms.py:23
+msgid "Path to the log file"
+msgstr "Locatie van logbestand"
+
+#: forms.py:25
+msgid "Path to log file used to collect statistics"
+msgstr "Locatie van logbestand gebruik om statistieken te maken"
+
+#: forms.py:30
+msgid "Directory to store RRD files"
+msgstr "Map om RRD bestanden op te slaan"
+
+#: forms.py:33
+msgid "Path to directory where RRD files are stored"
+msgstr "Locatie van map waar RRD bestanden staan"
+
+#: forms.py:38
+#, fuzzy
+#| msgid "rejected messages"
+msgid "Show greylisted messages"
+msgstr "geweigerde berichten"
+
+#: forms.py:41
+msgid "Differentiate between hard and soft rejects (greylisting)"
+msgstr ""
+
+#: forms.py:59 handlers.py:22
+msgid "Statistics"
+msgstr "Statistieken"
+
+#: graphics.py:95
+msgid "Failed to locate rrdtool binary."
+msgstr ""
+
+#: graphics.py:174
+msgid "Average traffic (msgs/min)"
+msgstr "Gemiddeld verkeer per minuut (berichten)"
+
+#: graphics.py:177
+msgid "sent messages"
+msgstr "verstuurde berichten"
+
+#: graphics.py:178
+msgid "received messages"
+msgstr "ontvangen berichten"
+
+#: graphics.py:179
+msgid "bounced messages"
+msgstr "geweigerde berichten"
+
+#: graphics.py:180
+msgid "rejected messages"
+msgstr "geweigerde berichten"
+
+#: graphics.py:181
+#, fuzzy
+#| msgid "sent messages"
+msgid "virus messages"
+msgstr "verstuurde berichten"
+
+#: graphics.py:182
+#, fuzzy
+#| msgid "sent messages"
+msgid "spam messages"
+msgstr "verstuurde berichten"
+
+#: graphics.py:189
+#, fuzzy
+#| msgid "rejected messages"
+msgid "greylisted messages"
+msgstr "geweigerde berichten"
+
+#: graphics.py:200
+msgid "Average normal traffic size (bytes/min)"
+msgstr "Gemiddelde bandbreette per minuut (bytes)"
+
+#: graphics.py:203
+msgid "received size"
+msgstr "ontvangen grootte"
+
+#: graphics.py:205
+msgid "sent size"
+msgstr "verstuurde grootte"
+
+#: graphics.py:213
+msgid "Mail traffic"
+msgstr "Mailverkeer"
+
+#: graphics.py:258
+msgid "Average account creation (account/hour)"
+msgstr ""
+
+#: graphics.py:261
+msgid "New accounts"
+msgstr ""
+
+#: graphics.py:268
+msgid "Accounts"
+msgstr ""
+
+#: templates/maillog/index.html:4
+msgid "Graphical statistics"
+msgstr "Grafische statistieken"
+
+#: templates/maillog/index.html:44
+msgid "Search a domain"
+msgstr "Zoek een domein"
+
+#: templates/maillog/index.html:77
+msgid "Apply"
+msgstr "Toepassen"
+
+#: views.py:33
+msgid "No statistics available"
+msgstr "Geen statistieken beschikbaar"
+
+#: views.py:40
+msgid "Day"
+msgstr "Dag"
+
+#: views.py:41
+msgid "Week"
+msgstr "Week"
+
+#: views.py:42
+msgid "Month"
+msgstr "Maand"
+
+#: views.py:43
+msgid "Year"
+msgstr "Jaar"
+
+#: views.py:44
+msgid "Custom"
+msgstr "Aangepast"
+
+#: views.py:63
+msgid "Unknown graphic set"
+msgstr "Onbekende grafische set"
+
+#: views.py:68
+#, fuzzy
+#| msgid "Unknown graphic set"
+msgid "Unknown domain"
+msgstr "Onbekende grafische set"
+
+#: views.py:73
+msgid "Bad custom period"
+msgstr "Ongeldige aangepaste periode"
+
+#~ msgid "Graphical statistics about emails traffic using RRDtool"
+#~ msgstr "Grafische statistieken over emailverkeer dmv. RRDtool"
diff --git a/modoboa/maillog/locale/nl_NL/LC_MESSAGES/djangojs.po b/modoboa/maillog/locale/nl_NL/LC_MESSAGES/djangojs.po
new file mode 100644
index 000000000..d863fc318
--- /dev/null
+++ b/modoboa/maillog/locale/nl_NL/LC_MESSAGES/djangojs.po
@@ -0,0 +1,25 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Translators:
+# TuxBrother , 2014
+# TuxBrother , 2014
+msgid ""
+msgstr ""
+"Project-Id-Version: Modoboa\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2017-03-03 09:37+0100\n"
+"PO-Revision-Date: 2014-12-31 11:42+0000\n"
+"Last-Translator: TuxBrother \n"
+"Language-Team: Dutch (Netherlands) (http://www.transifex.com/projects/p/"
+"modoboa/language/nl_NL/)\n"
+"Language: nl_NL\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: static/modoboa_stats/js/stats.js:43
+msgid "Search a domain"
+msgstr "Zoek een domein"
diff --git a/modoboa/maillog/locale/pl_PL/LC_MESSAGES/django.po b/modoboa/maillog/locale/pl_PL/LC_MESSAGES/django.po
new file mode 100644
index 000000000..ecd15f35a
--- /dev/null
+++ b/modoboa/maillog/locale/pl_PL/LC_MESSAGES/django.po
@@ -0,0 +1,172 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Translators:
+# sin88 , 2016
+# Tomasz Proć , 2017
+msgid ""
+msgstr ""
+"Project-Id-Version: Modoboa\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-09-17 11:24+0200\n"
+"PO-Revision-Date: 2017-12-01 16:06+0000\n"
+"Last-Translator: Tomasz Proć \n"
+"Language-Team: Polish (Poland) (http://www.transifex.com/tonio/modoboa/"
+"language/pl_PL/)\n"
+"Language: pl_PL\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n"
+"%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n"
+"%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);\n"
+
+#: forms.py:20
+msgid "General"
+msgstr "Ogólne"
+
+#: forms.py:23
+msgid "Path to the log file"
+msgstr "Ścieżka do pliku z logiem"
+
+#: forms.py:25
+msgid "Path to log file used to collect statistics"
+msgstr "Sćieżka do pliku z logiem dla colect "
+
+#: forms.py:30
+msgid "Directory to store RRD files"
+msgstr "Katalog do przechowywania plików RRD"
+
+#: forms.py:33
+msgid "Path to directory where RRD files are stored"
+msgstr "Katalog gdzie przechowywane są pliki RRD"
+
+#: forms.py:38
+msgid "Show greylisted messages"
+msgstr "Pokaż greylistowane wiadomości"
+
+#: forms.py:41
+msgid "Differentiate between hard and soft rejects (greylisting)"
+msgstr "Rozróżniaj twarde i miękkie odrzuty (greylisting)"
+
+#: forms.py:59 handlers.py:22
+msgid "Statistics"
+msgstr "Statystyki"
+
+#: graphics.py:95
+msgid "Failed to locate rrdtool binary."
+msgstr "Nie znaleziono binarki rrdtool."
+
+#: graphics.py:174
+msgid "Average traffic (msgs/min)"
+msgstr "Średni ruch (wiadomości/minutę)"
+
+#: graphics.py:177
+msgid "sent messages"
+msgstr "Wysłane wiadomości"
+
+#: graphics.py:178
+msgid "received messages"
+msgstr "Odebrane wiadomości"
+
+#: graphics.py:179
+msgid "bounced messages"
+msgstr "Odbite wiadomości"
+
+#: graphics.py:180
+msgid "rejected messages"
+msgstr "Odrzucone wiadomości"
+
+#: graphics.py:181
+msgid "virus messages"
+msgstr "wiadomości zawirusowane"
+
+#: graphics.py:182
+msgid "spam messages"
+msgstr "wiadomości spam"
+
+#: graphics.py:189
+msgid "greylisted messages"
+msgstr "wiadomości opóźnione (greylisting)"
+
+#: graphics.py:200
+msgid "Average normal traffic size (bytes/min)"
+msgstr "Średnia normalna wielkość ruchu (bajtów/min)"
+
+#: graphics.py:203
+msgid "received size"
+msgstr "Ilość odebranych danych"
+
+#: graphics.py:205
+msgid "sent size"
+msgstr "Ilość wysłanych danych"
+
+#: graphics.py:213
+msgid "Mail traffic"
+msgstr "Ruch e-mail"
+
+#: graphics.py:258
+msgid "Average account creation (account/hour)"
+msgstr "Średnia ilość tworzonych kont (konto/godzina)"
+
+#: graphics.py:261
+msgid "New accounts"
+msgstr "Nowe konta"
+
+#: graphics.py:268
+msgid "Accounts"
+msgstr "Konta"
+
+#: templates/maillog/index.html:4
+msgid "Graphical statistics"
+msgstr "Graficzne statytyki"
+
+#: templates/maillog/index.html:44
+msgid "Search a domain"
+msgstr "Wyszukaj domenę"
+
+#: templates/maillog/index.html:77
+msgid "Apply"
+msgstr "Zastosuj"
+
+#: views.py:33
+msgid "No statistics available"
+msgstr "Brak dostępnych statysty"
+
+#: views.py:40
+msgid "Day"
+msgstr "Dzień"
+
+#: views.py:41
+msgid "Week"
+msgstr "Tydzień"
+
+#: views.py:42
+msgid "Month"
+msgstr "Miesiąc"
+
+#: views.py:43
+msgid "Year"
+msgstr "Rok"
+
+#: views.py:44
+msgid "Custom"
+msgstr "Własne"
+
+#: views.py:63
+msgid "Unknown graphic set"
+msgstr "Unknown graphic set"
+
+#: views.py:68
+#, fuzzy
+#| msgid "Unknown graphic set"
+msgid "Unknown domain"
+msgstr "Unknown graphic set"
+
+#: views.py:73
+msgid "Bad custom period"
+msgstr "Nieprawudłowy przedział czasowy"
+
+#~ msgid "Graphical statistics about emails traffic using RRDtool"
+#~ msgstr "Graficzne statystyki wiadomości z wykorzystaniem "
diff --git a/modoboa/maillog/locale/pl_PL/LC_MESSAGES/djangojs.po b/modoboa/maillog/locale/pl_PL/LC_MESSAGES/djangojs.po
new file mode 100644
index 000000000..3b9b9304a
--- /dev/null
+++ b/modoboa/maillog/locale/pl_PL/LC_MESSAGES/djangojs.po
@@ -0,0 +1,23 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Translators:
+# sin88 , 2016
+msgid ""
+msgstr ""
+"Project-Id-Version: Modoboa\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2017-03-03 09:37+0100\n"
+"PO-Revision-Date: 2017-09-22 09:57+0000\n"
+"Last-Translator: sin88 \n"
+"Language-Team: Polish (Poland) (http://www.transifex.com/tonio/modoboa/language/pl_PL/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: pl_PL\n"
+"Plural-Forms: nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);\n"
+
+#: static/modoboa_stats/js/stats.js:43
+msgid "Search a domain"
+msgstr "Wyszukaj domenę"
diff --git a/modoboa/maillog/locale/pt_BR/LC_MESSAGES/django.po b/modoboa/maillog/locale/pt_BR/LC_MESSAGES/django.po
new file mode 100644
index 000000000..7f34c5b1e
--- /dev/null
+++ b/modoboa/maillog/locale/pt_BR/LC_MESSAGES/django.po
@@ -0,0 +1,179 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Translators:
+# csmk, 2014
+# Paulino Michelazzo , 2014
+# Rafael Barretto Alves , 2013
+msgid ""
+msgstr ""
+"Project-Id-Version: Modoboa\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-09-17 11:24+0200\n"
+"PO-Revision-Date: 2014-12-13 09:10+0000\n"
+"Last-Translator: Paulino Michelazzo \n"
+"Language-Team: Portuguese (Brazil) (http://www.transifex.com/projects/p/"
+"modoboa/language/pt_BR/)\n"
+"Language: pt_BR\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+
+#: forms.py:20
+msgid "General"
+msgstr "Geral"
+
+#: forms.py:23
+msgid "Path to the log file"
+msgstr "Caminho para o arquivo de log"
+
+#: forms.py:25
+msgid "Path to log file used to collect statistics"
+msgstr "Caminho para o arquivo de log usado para coletar estatísticas"
+
+#: forms.py:30
+msgid "Directory to store RRD files"
+msgstr "Diretório para armazenar arquivos RRD"
+
+#: forms.py:33
+msgid "Path to directory where RRD files are stored"
+msgstr "Caminho para o diretório onde os arquivos RRD são armazenados"
+
+#: forms.py:38
+#, fuzzy
+#| msgid "rejected messages"
+msgid "Show greylisted messages"
+msgstr "mensagens rejeitadas"
+
+#: forms.py:41
+msgid "Differentiate between hard and soft rejects (greylisting)"
+msgstr ""
+
+#: forms.py:59 handlers.py:22
+msgid "Statistics"
+msgstr "Estatísticas"
+
+#: graphics.py:95
+msgid "Failed to locate rrdtool binary."
+msgstr ""
+
+#: graphics.py:174
+msgid "Average traffic (msgs/min)"
+msgstr "Tráfego médio (msgs/min)"
+
+#: graphics.py:177
+msgid "sent messages"
+msgstr "mensagens enviadas"
+
+#: graphics.py:178
+msgid "received messages"
+msgstr "mensagens recebidas"
+
+#: graphics.py:179
+msgid "bounced messages"
+msgstr "mensagens devolvidas"
+
+#: graphics.py:180
+msgid "rejected messages"
+msgstr "mensagens rejeitadas"
+
+#: graphics.py:181
+#, fuzzy
+#| msgid "sent messages"
+msgid "virus messages"
+msgstr "mensagens enviadas"
+
+#: graphics.py:182
+#, fuzzy
+#| msgid "sent messages"
+msgid "spam messages"
+msgstr "mensagens enviadas"
+
+#: graphics.py:189
+#, fuzzy
+#| msgid "rejected messages"
+msgid "greylisted messages"
+msgstr "mensagens rejeitadas"
+
+#: graphics.py:200
+msgid "Average normal traffic size (bytes/min)"
+msgstr "Tamanho médio de tráfego (bytes/min)"
+
+#: graphics.py:203
+msgid "received size"
+msgstr "tamanho recebido"
+
+#: graphics.py:205
+msgid "sent size"
+msgstr "tamanho enviado"
+
+#: graphics.py:213
+msgid "Mail traffic"
+msgstr "Tráfego de e-mail"
+
+#: graphics.py:258
+msgid "Average account creation (account/hour)"
+msgstr ""
+
+#: graphics.py:261
+msgid "New accounts"
+msgstr ""
+
+#: graphics.py:268
+msgid "Accounts"
+msgstr ""
+
+#: templates/maillog/index.html:4
+msgid "Graphical statistics"
+msgstr "Estatísticas gráficas"
+
+#: templates/maillog/index.html:44
+msgid "Search a domain"
+msgstr "Buscar um domínio"
+
+#: templates/maillog/index.html:77
+msgid "Apply"
+msgstr "Aplicar"
+
+#: views.py:33
+msgid "No statistics available"
+msgstr "Não há estatísticas disponíveis"
+
+#: views.py:40
+msgid "Day"
+msgstr "Dia"
+
+#: views.py:41
+msgid "Week"
+msgstr "Semana"
+
+#: views.py:42
+msgid "Month"
+msgstr "Mês"
+
+#: views.py:43
+msgid "Year"
+msgstr "Ano"
+
+#: views.py:44
+msgid "Custom"
+msgstr "Customizado"
+
+#: views.py:63
+msgid "Unknown graphic set"
+msgstr "Conjunto gráfico desconhecido"
+
+#: views.py:68
+#, fuzzy
+#| msgid "Unknown graphic set"
+msgid "Unknown domain"
+msgstr "Conjunto gráfico desconhecido"
+
+#: views.py:73
+msgid "Bad custom period"
+msgstr "Período customizado inválido"
+
+#~ msgid "Graphical statistics about emails traffic using RRDtool"
+#~ msgstr "Estatísticas gráficas sobre o tráfego de e-mails usando RRDtool"
diff --git a/modoboa/maillog/locale/pt_BR/LC_MESSAGES/djangojs.po b/modoboa/maillog/locale/pt_BR/LC_MESSAGES/djangojs.po
new file mode 100644
index 000000000..8ed8fade5
--- /dev/null
+++ b/modoboa/maillog/locale/pt_BR/LC_MESSAGES/djangojs.po
@@ -0,0 +1,24 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Translators:
+# Paulino Michelazzo , 2014
+msgid ""
+msgstr ""
+"Project-Id-Version: Modoboa\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2017-03-03 09:37+0100\n"
+"PO-Revision-Date: 2014-12-13 09:14+0000\n"
+"Last-Translator: Paulino Michelazzo \n"
+"Language-Team: Portuguese (Brazil) (http://www.transifex.com/projects/p/"
+"modoboa/language/pt_BR/)\n"
+"Language: pt_BR\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+
+#: static/modoboa_stats/js/stats.js:43
+msgid "Search a domain"
+msgstr "Buscar um domínio"
diff --git a/modoboa/maillog/locale/pt_PT/LC_MESSAGES/django.po b/modoboa/maillog/locale/pt_PT/LC_MESSAGES/django.po
new file mode 100644
index 000000000..5f0a06ada
--- /dev/null
+++ b/modoboa/maillog/locale/pt_PT/LC_MESSAGES/django.po
@@ -0,0 +1,182 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Translators:
+# Damien Duponchelle , 2013
+# lusitan , 2013
+# Mike C. , 2012
+# Mike C. , 2013-2014
+# Sandra Ribeiro , 2012-2013
+# Sandra Ribeiro , 2012
+msgid ""
+msgstr ""
+"Project-Id-Version: Modoboa\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-09-17 11:24+0200\n"
+"PO-Revision-Date: 2014-12-29 12:51+0000\n"
+"Last-Translator: Mike C. \n"
+"Language-Team: Portuguese (Portugal) (http://www.transifex.com/projects/p/"
+"modoboa/language/pt_PT/)\n"
+"Language: pt_PT\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: forms.py:20
+msgid "General"
+msgstr "Geral"
+
+#: forms.py:23
+msgid "Path to the log file"
+msgstr "Caminho para o ficheiro log"
+
+#: forms.py:25
+msgid "Path to log file used to collect statistics"
+msgstr "Caminho para o ficheiro de log usado para recolher as estatísticas"
+
+#: forms.py:30
+msgid "Directory to store RRD files"
+msgstr "Diretório para armazenar os ficheiros RRD"
+
+#: forms.py:33
+msgid "Path to directory where RRD files are stored"
+msgstr "Caminho para o diretório onde os ficheiros RRD são guardados"
+
+#: forms.py:38
+#, fuzzy
+#| msgid "rejected messages"
+msgid "Show greylisted messages"
+msgstr "mensagens rejeitadas"
+
+#: forms.py:41
+msgid "Differentiate between hard and soft rejects (greylisting)"
+msgstr ""
+
+#: forms.py:59 handlers.py:22
+msgid "Statistics"
+msgstr "Estatísticas"
+
+#: graphics.py:95
+msgid "Failed to locate rrdtool binary."
+msgstr ""
+
+#: graphics.py:174
+msgid "Average traffic (msgs/min)"
+msgstr "Trafego em média (msgs/min)"
+
+#: graphics.py:177
+msgid "sent messages"
+msgstr "mensagens enviadas"
+
+#: graphics.py:178
+msgid "received messages"
+msgstr "mensagens recebidas"
+
+#: graphics.py:179
+msgid "bounced messages"
+msgstr "mensagens devolvidas"
+
+#: graphics.py:180
+msgid "rejected messages"
+msgstr "mensagens rejeitadas"
+
+#: graphics.py:181
+#, fuzzy
+#| msgid "sent messages"
+msgid "virus messages"
+msgstr "mensagens enviadas"
+
+#: graphics.py:182
+#, fuzzy
+#| msgid "sent messages"
+msgid "spam messages"
+msgstr "mensagens enviadas"
+
+#: graphics.py:189
+#, fuzzy
+#| msgid "rejected messages"
+msgid "greylisted messages"
+msgstr "mensagens rejeitadas"
+
+#: graphics.py:200
+msgid "Average normal traffic size (bytes/min)"
+msgstr "Tráfego em média normal por tamanho (bytes/min)"
+
+#: graphics.py:203
+msgid "received size"
+msgstr "tamanho recebido"
+
+#: graphics.py:205
+msgid "sent size"
+msgstr "tamanho enviado"
+
+#: graphics.py:213
+msgid "Mail traffic"
+msgstr "Tráfego de correio"
+
+#: graphics.py:258
+msgid "Average account creation (account/hour)"
+msgstr ""
+
+#: graphics.py:261
+msgid "New accounts"
+msgstr ""
+
+#: graphics.py:268
+msgid "Accounts"
+msgstr ""
+
+#: templates/maillog/index.html:4
+msgid "Graphical statistics"
+msgstr "Estatísticas gráficas"
+
+#: templates/maillog/index.html:44
+msgid "Search a domain"
+msgstr "Procurar um domínio"
+
+#: templates/maillog/index.html:77
+msgid "Apply"
+msgstr "Aplicar"
+
+#: views.py:33
+msgid "No statistics available"
+msgstr "Não há estatísticas disponíveis"
+
+#: views.py:40
+msgid "Day"
+msgstr "Dia"
+
+#: views.py:41
+msgid "Week"
+msgstr "Semana"
+
+#: views.py:42
+msgid "Month"
+msgstr "Mês"
+
+#: views.py:43
+msgid "Year"
+msgstr "Ano"
+
+#: views.py:44
+msgid "Custom"
+msgstr "Personalizado"
+
+#: views.py:63
+msgid "Unknown graphic set"
+msgstr "Conjunto de gráficos desconhecido"
+
+#: views.py:68
+#, fuzzy
+#| msgid "Unknown graphic set"
+msgid "Unknown domain"
+msgstr "Conjunto de gráficos desconhecido"
+
+#: views.py:73
+msgid "Bad custom period"
+msgstr "Período personalizado inválido"
+
+#~ msgid "Graphical statistics about emails traffic using RRDtool"
+#~ msgstr "Estatísticas gráficas sobre tráfego de email usando RRDtool"
diff --git a/modoboa/maillog/locale/pt_PT/LC_MESSAGES/djangojs.po b/modoboa/maillog/locale/pt_PT/LC_MESSAGES/djangojs.po
new file mode 100644
index 000000000..1d7e6e4c3
--- /dev/null
+++ b/modoboa/maillog/locale/pt_PT/LC_MESSAGES/djangojs.po
@@ -0,0 +1,29 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Translators:
+# lusitan , 2013
+# lusitan , 2013
+# Mike C. , 2012
+# Mike C. , 2012
+# Mike C. , 2014
+# Sandra Ribeiro , 2013
+msgid ""
+msgstr ""
+"Project-Id-Version: Modoboa\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2017-03-03 09:37+0100\n"
+"PO-Revision-Date: 2014-12-29 12:08+0000\n"
+"Last-Translator: Mike C. \n"
+"Language-Team: Portuguese (Portugal) (http://www.transifex.com/projects/p/"
+"modoboa/language/pt_PT/)\n"
+"Language: pt_PT\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: static/modoboa_stats/js/stats.js:43
+msgid "Search a domain"
+msgstr "Pesquisar domínio"
diff --git a/modoboa/maillog/locale/ro_RO/LC_MESSAGES/django.po b/modoboa/maillog/locale/ro_RO/LC_MESSAGES/django.po
new file mode 100644
index 000000000..68a4fe92b
--- /dev/null
+++ b/modoboa/maillog/locale/ro_RO/LC_MESSAGES/django.po
@@ -0,0 +1,170 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Translators:
+# Andrei-Ilie Olteanu , 2018
+msgid ""
+msgstr ""
+"Project-Id-Version: Modoboa\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-09-17 11:24+0200\n"
+"PO-Revision-Date: 2018-06-12 15:03+0000\n"
+"Last-Translator: Andrei-Ilie Olteanu \n"
+"Language-Team: Romanian (Romania) (http://www.transifex.com/tonio/modoboa/"
+"language/ro_RO/)\n"
+"Language: ro_RO\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n==1?0:(((n%100>19)||((n%100==0)&&(n!=0)))?"
+"2:1));\n"
+
+#: forms.py:20
+msgid "General"
+msgstr "General"
+
+#: forms.py:23
+msgid "Path to the log file"
+msgstr "Calea către fișierul de log"
+
+#: forms.py:25
+msgid "Path to log file used to collect statistics"
+msgstr "Calea către fișierul de log în care se colectează statistici"
+
+#: forms.py:30
+msgid "Directory to store RRD files"
+msgstr "Directorul în care se stochează fisierele RRD"
+
+#: forms.py:33
+msgid "Path to directory where RRD files are stored"
+msgstr "Calea către directorul în care se stochează fisierele RRD"
+
+#: forms.py:38
+msgid "Show greylisted messages"
+msgstr "Arată mesajele suspecte"
+
+#: forms.py:41
+msgid "Differentiate between hard and soft rejects (greylisting)"
+msgstr "Diferențiază între mesjale rejectate hard și soft (suspecte)"
+
+#: forms.py:59 handlers.py:22
+msgid "Statistics"
+msgstr "Statistici"
+
+#: graphics.py:95
+msgid "Failed to locate rrdtool binary."
+msgstr "Nu s-a gasit binarul rrdtool"
+
+#: graphics.py:174
+msgid "Average traffic (msgs/min)"
+msgstr "Media traficului (mesaje/min)"
+
+#: graphics.py:177
+msgid "sent messages"
+msgstr "mesaje trimise"
+
+#: graphics.py:178
+msgid "received messages"
+msgstr "mesaje primite "
+
+#: graphics.py:179
+msgid "bounced messages"
+msgstr "mesaje returnate"
+
+#: graphics.py:180
+msgid "rejected messages"
+msgstr "mesaje rejectate"
+
+#: graphics.py:181
+msgid "virus messages"
+msgstr "mesaje cu virus"
+
+#: graphics.py:182
+msgid "spam messages"
+msgstr "mesaje spam"
+
+#: graphics.py:189
+msgid "greylisted messages"
+msgstr "mesaje suspecte"
+
+#: graphics.py:200
+msgid "Average normal traffic size (bytes/min)"
+msgstr "Media de trafic (octecți/minut)"
+
+#: graphics.py:203
+msgid "received size"
+msgstr "mărime pentru primite"
+
+#: graphics.py:205
+msgid "sent size"
+msgstr "mărime pentru trimise"
+
+#: graphics.py:213
+msgid "Mail traffic"
+msgstr "Trafic Email"
+
+#: graphics.py:258
+msgid "Average account creation (account/hour)"
+msgstr "Media creări de conturi (cont/oră)"
+
+#: graphics.py:261
+msgid "New accounts"
+msgstr "Conturi noi"
+
+#: graphics.py:268
+msgid "Accounts"
+msgstr "Conturi"
+
+#: templates/maillog/index.html:4
+msgid "Graphical statistics"
+msgstr "Statistici grafice"
+
+#: templates/maillog/index.html:44
+msgid "Search a domain"
+msgstr "Cauta un domeniu"
+
+#: templates/maillog/index.html:77
+msgid "Apply"
+msgstr "Aplică"
+
+#: views.py:33
+msgid "No statistics available"
+msgstr "Nu sunt disponibile statistici"
+
+#: views.py:40
+msgid "Day"
+msgstr "Zi"
+
+#: views.py:41
+msgid "Week"
+msgstr "Săptămană"
+
+#: views.py:42
+msgid "Month"
+msgstr "Lună"
+
+#: views.py:43
+msgid "Year"
+msgstr "An"
+
+#: views.py:44
+msgid "Custom"
+msgstr "Personalizat"
+
+#: views.py:63
+msgid "Unknown graphic set"
+msgstr "Set grafic necunoscut"
+
+#: views.py:68
+#, fuzzy
+#| msgid "Unknown graphic set"
+msgid "Unknown domain"
+msgstr "Set grafic necunoscut"
+
+#: views.py:73
+msgid "Bad custom period"
+msgstr "Perioada personalizată invalidă"
+
+#~ msgid "Graphical statistics about emails traffic using RRDtool"
+#~ msgstr "Statistici grafice despre traficul pe email folosind RRDtool"
diff --git a/modoboa/maillog/locale/ro_RO/LC_MESSAGES/djangojs.po b/modoboa/maillog/locale/ro_RO/LC_MESSAGES/djangojs.po
new file mode 100644
index 000000000..78d5ddd1f
--- /dev/null
+++ b/modoboa/maillog/locale/ro_RO/LC_MESSAGES/djangojs.po
@@ -0,0 +1,23 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Translators:
+# Andrei-Ilie Olteanu , 2018
+msgid ""
+msgstr ""
+"Project-Id-Version: Modoboa\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2017-03-03 09:37+0100\n"
+"PO-Revision-Date: 2018-06-12 15:04+0000\n"
+"Last-Translator: Andrei-Ilie Olteanu \n"
+"Language-Team: Romanian (Romania) (http://www.transifex.com/tonio/modoboa/language/ro_RO/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: ro_RO\n"
+"Plural-Forms: nplurals=3; plural=(n==1?0:(((n%100>19)||((n%100==0)&&(n!=0)))?2:1));\n"
+
+#: static/modoboa_stats/js/stats.js:43
+msgid "Search a domain"
+msgstr "Cauta un domeniu"
diff --git a/modoboa/maillog/locale/ru/LC_MESSAGES/django.po b/modoboa/maillog/locale/ru/LC_MESSAGES/django.po
new file mode 100644
index 000000000..53a02cbc9
--- /dev/null
+++ b/modoboa/maillog/locale/ru/LC_MESSAGES/django.po
@@ -0,0 +1,179 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Translators:
+msgid ""
+msgstr ""
+"Project-Id-Version: Modoboa\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-09-17 11:24+0200\n"
+"PO-Revision-Date: 2013-12-03 09:35+0000\n"
+"Last-Translator: Antoine Nguyen \n"
+"Language-Team: Russian (http://www.transifex.com/projects/p/modoboa/language/"
+"ru/)\n"
+"Language: ru\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
+"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
+
+#: forms.py:20
+msgid "General"
+msgstr "Основные"
+
+#: forms.py:23
+msgid "Path to the log file"
+msgstr "Путь к лог файлу"
+
+#: forms.py:25
+msgid "Path to log file used to collect statistics"
+msgstr "Путь к лог файлу для сбора статистики"
+
+#: forms.py:30
+msgid "Directory to store RRD files"
+msgstr "Папка для сохранения RRD файлов"
+
+#: forms.py:33
+msgid "Path to directory where RRD files are stored"
+msgstr "Путь к директории для сохранения RRD файлов"
+
+#: forms.py:38
+#, fuzzy
+#| msgid "rejected messages"
+msgid "Show greylisted messages"
+msgstr "Отвергнутые сообщения"
+
+#: forms.py:41
+msgid "Differentiate between hard and soft rejects (greylisting)"
+msgstr ""
+
+#: forms.py:59 handlers.py:22
+msgid "Statistics"
+msgstr "Статистика"
+
+#: graphics.py:95
+msgid "Failed to locate rrdtool binary."
+msgstr ""
+
+#: graphics.py:174
+#, fuzzy
+msgid "Average traffic (msgs/min)"
+msgstr "Средний размер траффика"
+
+#: graphics.py:177
+msgid "sent messages"
+msgstr "Отправлено сообщений"
+
+#: graphics.py:178
+msgid "received messages"
+msgstr "Принято сообщений"
+
+#: graphics.py:179
+msgid "bounced messages"
+msgstr "Возвращенные сообщения"
+
+#: graphics.py:180
+msgid "rejected messages"
+msgstr "Отвергнутые сообщения"
+
+#: graphics.py:181
+#, fuzzy
+#| msgid "sent messages"
+msgid "virus messages"
+msgstr "Отправлено сообщений"
+
+#: graphics.py:182
+#, fuzzy
+#| msgid "sent messages"
+msgid "spam messages"
+msgstr "Отправлено сообщений"
+
+#: graphics.py:189
+#, fuzzy
+#| msgid "rejected messages"
+msgid "greylisted messages"
+msgstr "Отвергнутые сообщения"
+
+#: graphics.py:200
+#, fuzzy
+msgid "Average normal traffic size (bytes/min)"
+msgstr "Средний размер траффика"
+
+#: graphics.py:203
+msgid "received size"
+msgstr "Принято"
+
+#: graphics.py:205
+msgid "sent size"
+msgstr "Отправлено"
+
+#: graphics.py:213
+msgid "Mail traffic"
+msgstr "Почтовый траффик"
+
+#: graphics.py:258
+msgid "Average account creation (account/hour)"
+msgstr ""
+
+#: graphics.py:261
+msgid "New accounts"
+msgstr ""
+
+#: graphics.py:268
+msgid "Accounts"
+msgstr ""
+
+#: templates/maillog/index.html:4
+msgid "Graphical statistics"
+msgstr "Графическая статистика"
+
+#: templates/maillog/index.html:44
+msgid "Search a domain"
+msgstr "Искать домен"
+
+#: templates/maillog/index.html:77
+msgid "Apply"
+msgstr "Применить"
+
+#: views.py:33
+msgid "No statistics available"
+msgstr "Статистика не доступна"
+
+#: views.py:40
+msgid "Day"
+msgstr "День"
+
+#: views.py:41
+msgid "Week"
+msgstr "Неделя"
+
+#: views.py:42
+msgid "Month"
+msgstr "Месяц"
+
+#: views.py:43
+msgid "Year"
+msgstr "Год"
+
+#: views.py:44
+msgid "Custom"
+msgstr ""
+
+#: views.py:63
+msgid "Unknown graphic set"
+msgstr "Неизвестный графический набор "
+
+#: views.py:68
+#, fuzzy
+#| msgid "Unknown graphic set"
+msgid "Unknown domain"
+msgstr "Неизвестный графический набор "
+
+#: views.py:73
+msgid "Bad custom period"
+msgstr "Не верный выбор периода"
+
+#~ msgid "Graphical statistics about emails traffic using RRDtool"
+#~ msgstr "Графическая статистика о почтовом траффика использует RRDtool"
diff --git a/modoboa/maillog/locale/ru/LC_MESSAGES/djangojs.po b/modoboa/maillog/locale/ru/LC_MESSAGES/djangojs.po
new file mode 100644
index 000000000..e35e123e6
--- /dev/null
+++ b/modoboa/maillog/locale/ru/LC_MESSAGES/djangojs.po
@@ -0,0 +1,24 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Translators:
+msgid ""
+msgstr ""
+"Project-Id-Version: Modoboa\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2017-03-03 09:37+0100\n"
+"PO-Revision-Date: 2013-12-03 09:35+0000\n"
+"Last-Translator: Antoine Nguyen \n"
+"Language-Team: Russian (http://www.transifex.com/projects/p/modoboa/language/"
+"ru/)\n"
+"Language: ru\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
+"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
+
+#: static/modoboa_stats/js/stats.js:43
+msgid "Search a domain"
+msgstr "Найти домен"
diff --git a/modoboa/maillog/locale/sv/LC_MESSAGES/django.po b/modoboa/maillog/locale/sv/LC_MESSAGES/django.po
new file mode 100644
index 000000000..e7072a06e
--- /dev/null
+++ b/modoboa/maillog/locale/sv/LC_MESSAGES/django.po
@@ -0,0 +1,179 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Translators:
+# Martin Persson , 2014
+# Olle Gustafsson , 2013
+# Olle Gustafsson , 2013,2015
+msgid ""
+msgstr ""
+"Project-Id-Version: Modoboa\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-09-17 11:24+0200\n"
+"PO-Revision-Date: 2015-01-13 15:18+0000\n"
+"Last-Translator: Olle Gustafsson \n"
+"Language-Team: Swedish (http://www.transifex.com/projects/p/modoboa/language/"
+"sv/)\n"
+"Language: sv\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: forms.py:20
+msgid "General"
+msgstr "Generellt"
+
+#: forms.py:23
+msgid "Path to the log file"
+msgstr "Sökväg till loggfil"
+
+#: forms.py:25
+msgid "Path to log file used to collect statistics"
+msgstr "Sökväg till loggfil som används för att samla in statistik"
+
+#: forms.py:30
+msgid "Directory to store RRD files"
+msgstr "Katalog för lagring av RRD filer"
+
+#: forms.py:33
+msgid "Path to directory where RRD files are stored"
+msgstr "Sökväg till katalog där RRD filer lagras"
+
+#: forms.py:38
+#, fuzzy
+#| msgid "rejected messages"
+msgid "Show greylisted messages"
+msgstr "avvisade meddelanden"
+
+#: forms.py:41
+msgid "Differentiate between hard and soft rejects (greylisting)"
+msgstr ""
+
+#: forms.py:59 handlers.py:22
+msgid "Statistics"
+msgstr "Statistik"
+
+#: graphics.py:95
+msgid "Failed to locate rrdtool binary."
+msgstr ""
+
+#: graphics.py:174
+msgid "Average traffic (msgs/min)"
+msgstr "Genomsnittlig trafik (meddelanden/min)"
+
+#: graphics.py:177
+msgid "sent messages"
+msgstr "skickade meddelanden"
+
+#: graphics.py:178
+msgid "received messages"
+msgstr "mottagna meddelande"
+
+#: graphics.py:179
+msgid "bounced messages"
+msgstr "studsade meddelanden"
+
+#: graphics.py:180
+msgid "rejected messages"
+msgstr "avvisade meddelanden"
+
+#: graphics.py:181
+#, fuzzy
+#| msgid "sent messages"
+msgid "virus messages"
+msgstr "skickade meddelanden"
+
+#: graphics.py:182
+#, fuzzy
+#| msgid "sent messages"
+msgid "spam messages"
+msgstr "skickade meddelanden"
+
+#: graphics.py:189
+#, fuzzy
+#| msgid "rejected messages"
+msgid "greylisted messages"
+msgstr "avvisade meddelanden"
+
+#: graphics.py:200
+msgid "Average normal traffic size (bytes/min)"
+msgstr "Genomsnittlig normal trafik (bytes/min)"
+
+#: graphics.py:203
+msgid "received size"
+msgstr "mottagen storlek"
+
+#: graphics.py:205
+msgid "sent size"
+msgstr "sänd storlek"
+
+#: graphics.py:213
+msgid "Mail traffic"
+msgstr "Mail trafik"
+
+#: graphics.py:258
+msgid "Average account creation (account/hour)"
+msgstr ""
+
+#: graphics.py:261
+msgid "New accounts"
+msgstr ""
+
+#: graphics.py:268
+msgid "Accounts"
+msgstr ""
+
+#: templates/maillog/index.html:4
+msgid "Graphical statistics"
+msgstr "Grafisk statistik"
+
+#: templates/maillog/index.html:44
+msgid "Search a domain"
+msgstr "Sök en domän"
+
+#: templates/maillog/index.html:77
+msgid "Apply"
+msgstr "Applicera"
+
+#: views.py:33
+msgid "No statistics available"
+msgstr "Ingen statistik tillgänglig"
+
+#: views.py:40
+msgid "Day"
+msgstr "Dag"
+
+#: views.py:41
+msgid "Week"
+msgstr "Vecka"
+
+#: views.py:42
+msgid "Month"
+msgstr "Månad"
+
+#: views.py:43
+msgid "Year"
+msgstr "År"
+
+#: views.py:44
+msgid "Custom"
+msgstr "Anpassad"
+
+#: views.py:63
+msgid "Unknown graphic set"
+msgstr "Okänt grafisk uppsättning"
+
+#: views.py:68
+#, fuzzy
+#| msgid "Unknown graphic set"
+msgid "Unknown domain"
+msgstr "Okänt grafisk uppsättning"
+
+#: views.py:73
+msgid "Bad custom period"
+msgstr "Ogiltig anpassad tid"
+
+#~ msgid "Graphical statistics about emails traffic using RRDtool"
+#~ msgstr "Grafisk statistik om e-post trafik med hjälp av RRDtool"
diff --git a/modoboa/maillog/locale/sv/LC_MESSAGES/djangojs.po b/modoboa/maillog/locale/sv/LC_MESSAGES/djangojs.po
new file mode 100644
index 000000000..396f6e3a2
--- /dev/null
+++ b/modoboa/maillog/locale/sv/LC_MESSAGES/djangojs.po
@@ -0,0 +1,25 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Translators:
+# Olle Gustafsson , 2013
+# Olle Gustafsson , 2013,2015
+msgid ""
+msgstr ""
+"Project-Id-Version: Modoboa\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2017-03-03 09:37+0100\n"
+"PO-Revision-Date: 2015-01-13 15:21+0000\n"
+"Last-Translator: Olle Gustafsson \n"
+"Language-Team: Swedish (http://www.transifex.com/projects/p/modoboa/language/"
+"sv/)\n"
+"Language: sv\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: static/modoboa_stats/js/stats.js:43
+msgid "Search a domain"
+msgstr "Sök domän"
diff --git a/modoboa/maillog/locale/zh_TW/LC_MESSAGES/django.po b/modoboa/maillog/locale/zh_TW/LC_MESSAGES/django.po
new file mode 100644
index 000000000..1edcb28dd
--- /dev/null
+++ b/modoboa/maillog/locale/zh_TW/LC_MESSAGES/django.po
@@ -0,0 +1,170 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Translators:
+# akong , 2018
+# Radium , 2017
+msgid ""
+msgstr ""
+"Project-Id-Version: Modoboa\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-09-17 11:24+0200\n"
+"PO-Revision-Date: 2018-06-28 09:01+0000\n"
+"Last-Translator: akong \n"
+"Language-Team: Chinese (Taiwan) (http://www.transifex.com/tonio/modoboa/"
+"language/zh_TW/)\n"
+"Language: zh_TW\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+
+#: forms.py:20
+msgid "General"
+msgstr "一般"
+
+#: forms.py:23
+msgid "Path to the log file"
+msgstr "日誌檔的路徑"
+
+#: forms.py:25
+msgid "Path to log file used to collect statistics"
+msgstr "日誌檔的路徑用於蒐集狀態"
+
+#: forms.py:30
+msgid "Directory to store RRD files"
+msgstr "儲存 RRD 檔案的目錄"
+
+#: forms.py:33
+msgid "Path to directory where RRD files are stored"
+msgstr "RRD 檔案已儲存到目錄路徑"
+
+#: forms.py:38
+msgid "Show greylisted messages"
+msgstr "顯示灰名單郵件"
+
+#: forms.py:41
+msgid "Differentiate between hard and soft rejects (greylisting)"
+msgstr "區分在硬的退件跟軟的退件之間 (灰名單)"
+
+#: forms.py:59 handlers.py:22
+msgid "Statistics"
+msgstr "統計"
+
+#: graphics.py:95
+msgid "Failed to locate rrdtool binary."
+msgstr "指定 rrdtools 位置失敗。"
+
+#: graphics.py:174
+msgid "Average traffic (msgs/min)"
+msgstr "平均流量 (郵件數/分鐘)"
+
+#: graphics.py:177
+msgid "sent messages"
+msgstr "已發訊息"
+
+#: graphics.py:178
+msgid "received messages"
+msgstr "收到的訊息"
+
+#: graphics.py:179
+msgid "bounced messages"
+msgstr "已退回郵件"
+
+#: graphics.py:180
+msgid "rejected messages"
+msgstr "已拒絕郵件"
+
+#: graphics.py:181
+msgid "virus messages"
+msgstr "病毒郵件"
+
+#: graphics.py:182
+msgid "spam messages"
+msgstr "垃圾郵件"
+
+#: graphics.py:189
+msgid "greylisted messages"
+msgstr "灰名單郵件"
+
+#: graphics.py:200
+msgid "Average normal traffic size (bytes/min)"
+msgstr "平均流量大小 (位元組/分鐘)"
+
+#: graphics.py:203
+msgid "received size"
+msgstr "已接收大小"
+
+#: graphics.py:205
+msgid "sent size"
+msgstr "寄送大小"
+
+#: graphics.py:213
+msgid "Mail traffic"
+msgstr "郵件流量"
+
+#: graphics.py:258
+msgid "Average account creation (account/hour)"
+msgstr "建立帳號平均值 (帳號/小時)"
+
+#: graphics.py:261
+msgid "New accounts"
+msgstr "新帳號"
+
+#: graphics.py:268
+msgid "Accounts"
+msgstr "帳號"
+
+#: templates/maillog/index.html:4
+msgid "Graphical statistics"
+msgstr "圖形狀態"
+
+#: templates/maillog/index.html:44
+msgid "Search a domain"
+msgstr "搜尋網域"
+
+#: templates/maillog/index.html:77
+msgid "Apply"
+msgstr "套用"
+
+#: views.py:33
+msgid "No statistics available"
+msgstr "沒有可用的狀態"
+
+#: views.py:40
+msgid "Day"
+msgstr "日"
+
+#: views.py:41
+msgid "Week"
+msgstr "周"
+
+#: views.py:42
+msgid "Month"
+msgstr "月"
+
+#: views.py:43
+msgid "Year"
+msgstr "年"
+
+#: views.py:44
+msgid "Custom"
+msgstr "自訂"
+
+#: views.py:63
+msgid "Unknown graphic set"
+msgstr "未知圖形設定"
+
+#: views.py:68
+#, fuzzy
+#| msgid "Unknown graphic set"
+msgid "Unknown domain"
+msgstr "未知圖形設定"
+
+#: views.py:73
+msgid "Bad custom period"
+msgstr "錯誤的自訂期間"
+
+#~ msgid "Graphical statistics about emails traffic using RRDtool"
+#~ msgstr "有關郵件流量的圖形狀態表是使用 RRDtool"
diff --git a/modoboa/maillog/locale/zh_TW/LC_MESSAGES/djangojs.po b/modoboa/maillog/locale/zh_TW/LC_MESSAGES/djangojs.po
new file mode 100644
index 000000000..6c0595e6a
--- /dev/null
+++ b/modoboa/maillog/locale/zh_TW/LC_MESSAGES/djangojs.po
@@ -0,0 +1,23 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Translators:
+# Radium , 2017
+msgid ""
+msgstr ""
+"Project-Id-Version: Modoboa\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2017-03-03 09:37+0100\n"
+"PO-Revision-Date: 2017-09-23 14:31+0000\n"
+"Last-Translator: Radium \n"
+"Language-Team: Chinese (Taiwan) (http://www.transifex.com/tonio/modoboa/language/zh_TW/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: zh_TW\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+
+#: static/modoboa_stats/js/stats.js:43
+msgid "Search a domain"
+msgstr "搜尋一個網域"
diff --git a/modoboa/maillog/management/__init__.py b/modoboa/maillog/management/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/modoboa/maillog/management/commands/__init__.py b/modoboa/maillog/management/commands/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/modoboa/maillog/management/commands/logparser.py b/modoboa/maillog/management/commands/logparser.py
new file mode 100644
index 000000000..87817fd2a
--- /dev/null
+++ b/modoboa/maillog/management/commands/logparser.py
@@ -0,0 +1,632 @@
+# coding: utf-8
+
+"""
+Postfix log parser.
+
+This scripts parses a log file produced by postfix (or using the same
+format). It looks for predefined events and build statistics about
+their occurence rate.
+
+At the, somes default graphics are generated using the grapher module.
+(see grapher.py)
+
+Predefined events are:
+ * Per domain sent/received messages,
+ * Per domain received bad messages (bounced, reject for now),
+ * Per domain sent/received traffics size,
+ * Global consolidation of all previous events.
+
+"""
+
+from datetime import datetime
+import io
+import os
+import re
+import sys
+import time
+
+import rrdtool
+
+from django.core.management.base import BaseCommand
+from django.db.models import Q
+from django.utils import timezone
+
+from modoboa.admin.models import Domain
+from modoboa.parameters import tools as param_tools
+from modoboa.lib.email_utils import split_mailbox
+
+from ... import lib
+from ... import models
+
+
+rrdstep = 60
+xpoints = 540
+points_per_sample = 3
+variables = ["sent", "recv", "bounced", "reject", "spam", "virus",
+ "size_sent", "size_recv"]
+
+
+class LogParser:
+
+ def __init__(self, options, workdir, year=None, greylist=False):
+ """Constructor."""
+ self.logfile = options["logfile"]
+ self.debug = options["debug"]
+ self.verbose = options["verbose"]
+ try:
+ self.f = io.open(self.logfile, encoding="utf-8")
+ except IOError as errno:
+ self._dprint("%s" % errno)
+ sys.exit(1)
+ self.workdir = workdir
+ self.__year = year
+ self.cfs = ["AVERAGE", "MAX"]
+
+ curtime = time.localtime()
+ if not self.__year:
+ self.__year = curtime.tm_year
+ self.curmonth = curtime.tm_mon
+
+ self.data = {"global": {}}
+ self.domains = []
+ self._load_domain_list()
+
+ self.workdict = {}
+ self.lupdates = {}
+
+ # set up regular expression
+ self._date_expressions = [
+ r"(?P\w+)\s+(?P\d+)\s+(?P\d+):(?P\d+):(?P\d+)(?P.*)", # noqa
+ r"(?P\d+)-(?P\d+)-(?P\d+)T(?P\d+):(?P\d+):(?P\d+)\.\d+\+\d+:\d+(?P.*)" # noqa
+ ]
+ self._date_expressions = [
+ re.compile(v) for v in self._date_expressions]
+ self.date_expr = None
+ self._regex = {
+ "line": r"\s+([-\w\.]+)\s+(\w+)/?(\w*)[[](\d+)[]]:\s+(.*)",
+ "id": r"(\w+): (.*)",
+ "reject": r"reject: .*from=<.*>,? to=<[^@]+@([^>]+)>",
+ "message-id": r"message-id=<([^>]*)>",
+ "from+size": r"from=<([^>]*)>, size=(\d+)",
+ "to+status": r"to=<([^>]*)>.*status=(\S+)",
+ "orig_to": r"orig_to=<([^>]*)>.*",
+ "amavis": r"(?PINFECTED|SPAM|SPAMMY) .* <[^>]+> -> <[^@]+@(?P[^>]+)>.*", # noqa
+ "rmilter_line": r"<(?P[0-9a-f]{10})>; (?P.*)",
+ "rmilter_msg_done": r"msg done: queue_id: <(?P[^>]+)>; message id: <(?P[^>]+)>.*; from: <(?P[^>]+)>; rcpt: <(?P[^>]+)>.*; spam scan: (?P[^;]+); virus scan:", # noqa
+ "rmilter_spam": r"mlfi_eom: (rejecting spam|add spam header to message according to spamd action)", # noqa
+ "rmilter_virus": r"mlfi_eom:.* virus found",
+ "rmilter_greylist": (
+ r"GREYLIST\([0-9]+\.[0-9]{2}\)\[greylisted[^\]]*\]")
+ }
+ self._regex = {k: re.compile(v) for k, v in self._regex.items()}
+ self._srs_regex = {
+ "detect_srs": "^SRS[01][+-=]",
+ "reverse_srs0": r"^SRS0[+-=]\S+=\S{2}=(\S+)=(\S+)\@\S+$",
+ "reverse_srs1": r"^SRS1[+-=]\S+=\S+==\S+=\S{2}=(\S+)=(\S+)\@\S+$"
+ }
+ self._srs_regex = {
+ k: re.compile(v, re.IGNORECASE) for k, v in self._srs_regex.items()
+ }
+
+ self.greylist = greylist
+ if greylist:
+ variables.insert(4, "greylist")
+ self._dprint("[settings] greylisting enabled")
+
+ self._prev_se = -1
+ self._prev_mi = -1
+ self._prev_ho = -1
+ self.cur_t = 0
+
+ def _load_domain_list(self):
+ """Load the list of allowed domains."""
+ for dom in Domain.objects.all():
+ domname = str(dom.name)
+ self.domains += [domname]
+ self.data[domname] = {}
+
+ # Also add alias domains
+ for alias in dom.domainalias_set.all():
+ aliasname = str(alias.name)
+ self.domains += [aliasname]
+ self.data[aliasname] = {}
+
+ def _dprint(self, msg):
+ """Print a debug message if required.
+
+ :param str msg: debug message
+ """
+ if not self.debug:
+ return
+ print(msg)
+
+ def _parse_date(self, line):
+ """Try to match a date inside :kw:`line` and to convert it to
+ a timestamp.
+
+ We try different date format until we find valid one. We then
+ store it for future use.
+
+ :param str line: a log entry
+ :return: the remaining part of the line or None
+ """
+ match = None
+ if self.date_expr is None:
+ for expr in self._date_expressions:
+ match = expr.match(line)
+ if match is not None:
+ self.date_expr = expr
+ break
+ else:
+ match = self.date_expr.match(line)
+ if match is None:
+ return None
+ ho = match.group("hour")
+ mi = match.group("min")
+ se = match.group("sec")
+ mo = match.group("month")
+ da = match.group("day")
+ try:
+ ye = match.group("year")
+ except IndexError:
+ ye = self.year(mo)
+ # Keep original timestamp
+ self.orig_ts = lib.date_to_timestamp([ye, mo, da, ho, mi, se])
+
+ # Now get the current period (based on rrdstep)
+ se = int(int(se) / rrdstep) # rrd step is one-minute => se = 0
+ if self._prev_se != se or self._prev_mi != mi or self._prev_ho != ho:
+ self.cur_t = lib.date_to_timestamp([ye, mo, da, ho, mi, se])
+ self.cur_t = self.cur_t - self.cur_t % rrdstep
+ self._prev_mi = mi
+ self._prev_ho = ho
+ self._prev_se = se
+ return match.group("eol")
+
+ def init_rrd(self, fname, m):
+ """init_rrd.
+
+ Set-up Data Sources (DS)
+ Set-up Round Robin Archives (RRA):
+ - day,week,month and year archives
+ - 2 types : AVERAGE and MAX
+
+ parameter : start time
+ return : last epoch recorded
+ """
+ ds_type = "ABSOLUTE"
+ rows = xpoints / points_per_sample
+ realrows = int(rows * 1.1) # ensure that the full range is covered
+ day_steps = int(3600 * 24 / (rrdstep * rows))
+ week_steps = day_steps * 7
+ month_steps = week_steps * 5
+ year_steps = month_steps * 12
+
+ # Set up data sources for our RRD
+ params = []
+ for v in variables:
+ params += ["DS:%s:%s:%s:0:U" % (v, ds_type, rrdstep * 2)]
+
+ # Set up RRD to archive data
+ for cf in ["AVERAGE", "MAX"]:
+ for step in [day_steps, week_steps, month_steps, year_steps]:
+ params += ["RRA:%s:0.5:%s:%s" % (cf, step, realrows)]
+
+ # With those setup, we can now created the RRD
+ rrdtool.create(str(fname),
+ "--start", str(m),
+ "--step", str(rrdstep),
+ *params)
+ return m
+
+ def add_datasource_to_rrd(self, fname, dsname):
+ """Add a new data source to an exisitng file.
+
+ Add missing Data Sources (DS) to existing Round Robin Archive (RRA):
+ See init_rrd for details.
+ """
+ ds_def = "DS:%s:ABSOLUTE:%s:0:U" % (dsname, rrdstep * 2)
+ rrdtool.tune(fname, ds_def)
+ self._dprint("[rrd] added DS %s to %s" % (dsname, fname))
+
+ def add_point_to_rrd(self, fname, tpl, values, ts=None):
+ """Try to add a new point to RRD file."""
+ if ts:
+ values = "{}:{}".format(ts, values)
+ if self.verbose:
+ print("[rrd] VERBOSE update -t %s %s" % (tpl, values))
+ try:
+ rrdtool.update(str(fname), "-t", tpl, values)
+ except rrdtool.OperationalError as e:
+ op_match = re.match(r"unknown DS name '(\w+)'", str(e))
+ if op_match is None:
+ raise
+ self.add_datasource_to_rrd(str(fname), op_match.group(1))
+ rrdtool.update(str(fname), "-t", tpl, values)
+
+ def update_rrd(self, dom, t):
+ """update_rrd
+
+ Update RRD with records at t time.
+
+ True : if data are up-to-date for current minute
+ False : syslog may have probably been already recorded
+ or something wrong
+ """
+ fname = "%s/%s.rrd" % (self.workdir, dom)
+ m = t - (t % rrdstep)
+
+ self._dprint("[rrd] updating %s" % fname)
+ if not os.path.exists(fname):
+ self.lupdates[fname] = self.init_rrd(fname, m)
+ self._dprint("[rrd] create new RRD file %s" % fname)
+ else:
+ if fname not in self.lupdates:
+ self.lupdates[fname] = rrdtool.last(str(fname))
+
+ if m <= self.lupdates[fname]:
+ if self.verbose:
+ print("[rrd] VERBOSE events at %s already recorded in RRD" % m)
+ return False
+
+ tpl = ""
+ for v in variables:
+ if tpl != "":
+ tpl += ":"
+ tpl += v
+ # Missing some RRD steps
+ # Est ce vraiment nécessaire... ?
+ if m > self.lupdates[fname] + rrdstep:
+ values = ""
+ for v in variables:
+ if values != "":
+ values += ":"
+ values += "0"
+ for p in range(self.lupdates[fname] + rrdstep, m, rrdstep):
+ self.add_point_to_rrd(fname, tpl, values, ts=p)
+
+ values = "%s" % m
+ tpl = ""
+ for v in variables:
+ values += ":"
+ values += str(self.data[dom][m][v])
+ if tpl != "":
+ tpl += ":"
+ tpl += v
+ self.add_point_to_rrd(fname, tpl, values)
+ self.lupdates[fname] = m
+ return True
+
+ def initcounters(self, dom):
+ init = {}
+ for v in variables:
+ init[v] = 0
+ self.data[dom][self.cur_t] = init
+
+ def inc_counter(self, dom, counter, val=1):
+ if dom is not None and dom in self.domains:
+ if self.cur_t not in self.data[dom]:
+ self.initcounters(dom)
+ self.data[dom][self.cur_t][counter] += val
+
+ if self.cur_t not in self.data["global"]:
+ self.initcounters("global")
+ self.data["global"][self.cur_t][counter] += val
+
+ def year(self, month):
+ """Return the appropriate year
+
+ Date used in log files don't always embark the year so we need
+ to guess it :p
+
+ This method tries to deal with year changes in a simply
+ (ugly?) way: if we currently are in january and the given
+ month is different, return the current year -1. Otherwise,
+ return the current year.
+
+ Obviously, this method only works for year to year + 1
+ changes.
+
+ :param month: the month of the current record beeing parsed
+ :return: an integer
+ """
+ month = time.strptime(month, "%b").tm_mon if not month.isdigit() \
+ else int(month)
+ if self.curmonth == 1 and month != self.curmonth:
+ return self.__year - 1
+ return self.__year
+
+ def is_srs_forward(self, mail_address):
+ """ Check if mail address has been mangled by SRS
+
+ Sender Rewriting Scheme (SRS) modifies mail adresses so that they
+ always end in a local domain.
+
+ :param str mail_address
+ :return a boolean
+ """
+ return self._srs_regex["detect_srs"].match(mail_address) is not None
+
+ def reverse_srs(self, mail_address):
+ """ Try to unwind a mail address rewritten by SRS
+
+ Sender Rewriting Scheme (SRS) modifies mail adresses so that they
+ always end in a local domain. Common Postfix implementations of SRS
+ rewrite all non-local mail addresss.
+
+ :param str mail_address
+ :return a str
+ """
+ m = self._srs_regex["reverse_srs0"].match(mail_address)
+ m = self._srs_regex["reverse_srs1"].match(mail_address) \
+ if m is None else m
+
+ if m is not None:
+ return "%s@%s" % m.group(2, 1)
+ else:
+ return mail_address
+
+ def _parse_amavis(self, log, host, pid, subprog):
+ """ Parse an Amavis log entry.
+
+ :param str log: logged message
+ :param str host: hostname
+ :param str pid: process ID
+ :return: True on success
+ """
+ m = self._regex["amavis"].search(log)
+ if m is not None:
+ dom = m.group("domain")
+ spam_result = m.group("result")
+ if dom is not None and dom in self.domains:
+ if spam_result == "INFECTED":
+ self.inc_counter(dom, "virus")
+ elif spam_result in ["SPAM", "SPAMMY"]:
+ self.inc_counter(dom, "spam")
+ return True
+
+ return False
+
+ def _parse_rmilter(self, log, host, pid, subprog):
+ """ Parse an Rmilter log entry.
+
+ :param str log: logged message
+ :param str host: hostname
+ :param str pid: process ID
+ :return: True on success
+ """
+ m = self._regex["rmilter_line"].match(log)
+ if m is None:
+ return False
+ rmilter_hash, msg = m.groups()
+ workdict_key = "rmilter_" + rmilter_hash
+
+ # Virus check must come before spam check due to pattern similarity.
+ m = self._regex["rmilter_virus"].match(msg)
+ if m is not None:
+ self.workdict[workdict_key] = {
+ "action": "virus"
+ }
+ return True
+ m = self._regex["rmilter_spam"].match(msg)
+ if m is not None:
+ self.workdict[workdict_key] = {
+ "action": "spam"
+ }
+ return True
+
+ # Greylisting
+ if self.greylist:
+ m = self._regex["rmilter_greylist"].search(msg)
+ if m is not None:
+ self.workdict[workdict_key] = {
+ "action": "greylist"
+ }
+ return True
+
+ # Gather information about message sender and queue ID
+ m = self._regex["rmilter_msg_done"].match(msg)
+ if m is not None:
+ dom = split_mailbox(m.group("rcpt"))[1]
+
+ if workdict_key in self.workdict:
+ action = self.workdict[workdict_key].get("action", None)
+ if action is not None:
+ self.inc_counter(dom, action)
+ return True
+
+ return False
+
+ def _parse_postfix(self, log, host, pid, subprog):
+ """ Parse a log entry generated by Postfix.
+
+ :param str log: logged message
+ :param str host: hostname
+ :param str pid: process ID
+ :return: True on success
+ """
+ m = self._regex["id"].match(log)
+ if m is None:
+ return False
+
+ queue_id, msg = m.groups()
+
+ # Handle rejected mails.
+ if queue_id == "NOQUEUE":
+ m = self._regex["reject"].match(msg)
+ dom = m.group(1) if m is not None else None
+ if dom in self.domains:
+ condition = (
+ self.greylist and (
+ "Greylisted" in msg or
+ (subprog == "postscreen" and " 450 " in msg))
+ )
+ if condition:
+ self.inc_counter(dom, "greylist")
+ else:
+ self.inc_counter(dom, "reject")
+ return True
+
+ # Message acknowledged.
+ m = self._regex["message-id"].search(msg)
+ if m is not None:
+ self.workdict[queue_id] = {"from": m.group(1), "size": 0}
+ return True
+
+ # Message enqueued.
+ m = self._regex["from+size"].search(msg)
+ if m is not None:
+ self.workdict[queue_id] = {
+ "from": self.reverse_srs(m.group(1)),
+ "size": int(m.group(2))
+ }
+ return True
+
+ # Message disposition.
+ m = self._regex["to+status"].search(msg)
+ if m is None:
+ return False
+ (msg_to, msg_status) = m.groups()
+ if queue_id not in self.workdict:
+ self._dprint("[parser] inconsistent mail (%s: %s), skipping"
+ % (queue_id, msg_to))
+ return True
+ if msg_status not in variables:
+ self._dprint("[parser] unsupported status %s, skipping"
+ % msg_status)
+ return True
+
+ # orig_to is optional.
+ m = self._regex["orig_to"].search(msg)
+ msg_orig_to = m.group(1) if m is not None else None
+
+ # Handle local "from" domains.
+ from_domain = split_mailbox(self.workdict[queue_id]["from"])[1]
+ if from_domain is not None and from_domain in self.domains:
+ self.inc_counter(from_domain, "sent")
+ self.inc_counter(from_domain, "size_sent",
+ self.workdict[queue_id]["size"])
+
+ # Handle local "to" domains.
+ to_domain = None
+ condition = (
+ msg_orig_to is not None and
+ not self.is_srs_forward(msg_orig_to)
+ )
+ if condition:
+ to_domain = split_mailbox(msg_orig_to)[1]
+ if to_domain is None:
+ to_domain = split_mailbox(msg_to)[1]
+
+ if msg_status == "sent":
+ self.inc_counter(to_domain, "recv")
+ self.inc_counter(to_domain, "size_recv",
+ self.workdict[queue_id]["size"])
+ else:
+ self.inc_counter(to_domain, msg_status)
+
+ last_message = models.Maillog.objects.last()
+ cur_dt = datetime.fromtimestamp(self.orig_ts)
+ tz = timezone.get_current_timezone()
+ cur_dt = tz.localize(cur_dt)
+ condition = (
+ last_message and (
+ cur_dt < last_message.date or
+ (cur_dt == last_message.date and
+ models.Maillog.objects.filter(
+ date=cur_dt, queue_id=queue_id).exists())
+ )
+ )
+ if not condition:
+ if from_domain in self.domains:
+ from_domain = Domain.objects.filter(
+ Q(name=from_domain) |
+ Q(domainalias__name=from_domain)
+ ).first()
+ else:
+ from_domain = None
+ if to_domain in self.domains:
+ to_domain = Domain.objects.filter(
+ Q(name=to_domain) |
+ Q(domainalias__name=to_domain)
+ ).first()
+ else:
+ to_domain = None
+ if msg_status == "sent" and to_domain:
+ msg_status = "received"
+ models.Maillog.objects.create(
+ queue_id=queue_id,
+ date=cur_dt,
+ sender=self.workdict[queue_id]["from"],
+ rcpt=msg_to,
+ original_rcpt=msg_orig_to,
+ size=self.workdict[queue_id]["size"],
+ status=msg_status,
+ from_domain=from_domain,
+ to_domain=to_domain
+ )
+
+ return True
+
+ def _parse_line(self, line):
+ """Parse a single log line.
+
+ :param str line: log line
+ """
+ line = self._parse_date(line)
+ if line is None:
+ return
+ m = self._regex["line"].match(line)
+ if not m:
+ return
+ host, prog, subprog, pid, log = m.groups()
+
+ try:
+ parser = getattr(self, "_parse_{}".format(prog))
+ if not parser(log, host, pid, subprog):
+ self._dprint("[parser] ignoring %r log: %r" % (prog, log))
+ except AttributeError:
+ self._dprint(
+ "[parser] no log handler for \"{}\": {}".format(prog, log))
+
+ def process(self):
+ """Process the log file.
+
+ We parse it and then generate standard graphics (day, week,
+ month).
+ """
+ for line in self.f.readlines():
+ self._parse_line(line)
+
+ for dom, data in self.data.items():
+ self._dprint("[rrd] dealing with domain %s" % dom)
+ for t in sorted(data.keys()):
+ self.update_rrd(dom, t)
+
+
+class Command(BaseCommand):
+ help = "Log file parser"
+
+ def add_arguments(self, parser):
+ """Add extra arguments to command line."""
+ parser.add_argument(
+ "--logfile", default=None,
+ help="postfix log in syslog format", metavar="FILE")
+ parser.add_argument(
+ "--verbose", default=False, action="store_true",
+ dest="verbose", help="Set verbose mode")
+ parser.add_argument(
+ "--debug", default=False, action="store_true",
+ help="Set debug mode")
+
+ def handle(self, *args, **options):
+ if options["logfile"] is None:
+ options["logfile"] = param_tools.get_global_parameter(
+ "logfile", app="maillog")
+ greylist = param_tools.get_global_parameter(
+ "greylist", raise_exception=False)
+ p = LogParser(
+ options, param_tools.get_global_parameter("rrd_rootdir"),
+ None, greylist
+ )
+ p.process()
diff --git a/modoboa/maillog/management/commands/update_statistics.py b/modoboa/maillog/management/commands/update_statistics.py
new file mode 100644
index 000000000..d828acab9
--- /dev/null
+++ b/modoboa/maillog/management/commands/update_statistics.py
@@ -0,0 +1,84 @@
+"""Management command to update various statistics."""
+
+import os
+
+from dateutil.relativedelta import relativedelta
+import rrdtool
+
+from django.core.management.base import BaseCommand
+from django.utils import timezone
+
+from modoboa.core import models as core_models
+from modoboa.parameters import tools as param_tools
+
+
+class Command(BaseCommand):
+ """Update statistics."""
+
+ help = "Update modoboa statistics"
+
+ def add_arguments(self, parser):
+ """Add extra arguments to command line."""
+ parser.add_argument(
+ "--rebuild", action="store_true",
+ help="Rebuild statistics from the begining")
+
+ def _create_new_accounts_rrd_file(self, fname, start):
+ """Create RRD file."""
+ step = 3600
+ ds_name = "new_accounts"
+ params = ["DS:{}:ABSOLUTE:{}:0:U".format(ds_name, step * 2)]
+ params += [
+ "RRA:AVERAGE:0.5:1:48", # 48 hours with a 1h granularity
+ "RRA:AVERAGE:0.5:24:31", # 31 days with a 1d granularity
+ "RRA:AVERAGE:0.5:168:52", # 52 weeks with a 1w granularity
+ "RRA:AVERAGE:0.5:5208:24", # 24 months with a 1m granularity
+ ]
+ rrdtool.create(
+ str(fname), "--start", str(start), "--step", str(step),
+ *params
+ )
+
+ def update_account_creation_stats(self, rebuild=False):
+ """Look for newly created accounts."""
+ db_path = os.path.join(self.rootdir, "new_accounts.rrd")
+ data = []
+ if not rebuild:
+ end = timezone.now().replace(minute=0, second=0, microsecond=0)
+ start = end - relativedelta(hours=1)
+ new_accounts = core_models.User.objects.filter(
+ date_joined__gte=start, date_joined__lt=end).count()
+ data.append(
+ "{}:{}".format(int(end.strftime("%s")), new_accounts * 60))
+ else:
+ existing_stats = {}
+ start = None
+ for user in core_models.User.objects.all().order_by("date_joined"):
+ hour = user.date_joined.replace(
+ minute=0, second=0, microsecond=0) + relativedelta(hours=1)
+ if start is None:
+ start = hour
+ hour = int(hour.strftime("%s"))
+ if hour not in existing_stats:
+ existing_stats[hour] = 0
+ existing_stats[hour] += 1
+ end = timezone.now().replace(
+ minute=0, second=0, microsecond=0) + relativedelta(hours=1)
+ if os.path.exists(db_path):
+ os.unlink(db_path)
+ hour = start
+ while hour <= end:
+ new_accounts = existing_stats.get(int(hour.strftime("%s")), 0)
+ data.append("{}:{}".format(
+ int(hour.strftime("%s")), new_accounts * 60))
+ hour += relativedelta(hours=1)
+ if not os.path.exists(db_path):
+ self._create_new_accounts_rrd_file(
+ db_path, int((start - relativedelta(hours=1)).strftime("%s"))
+ )
+ rrdtool.update(str(db_path), *data)
+
+ def handle(self, *args, **options):
+ """Entry point."""
+ self.rootdir = param_tools.get_global_parameter("rrd_rootdir")
+ self.update_account_creation_stats(options["rebuild"])
diff --git a/modoboa/maillog/migrations/0001_initial.py b/modoboa/maillog/migrations/0001_initial.py
new file mode 100644
index 000000000..92e1122eb
--- /dev/null
+++ b/modoboa/maillog/migrations/0001_initial.py
@@ -0,0 +1,34 @@
+# Generated by Django 2.2.12 on 2020-09-16 09:13
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ initial = True
+
+ dependencies = [
+ ('admin', '0017_alarm'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='Maillog',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('queue_id', models.CharField(max_length=50)),
+ ('date', models.DateTimeField()),
+ ('sender', models.EmailField(max_length=254)),
+ ('rcpt', models.EmailField(max_length=254)),
+ ('original_rcpt', models.EmailField(max_length=254, null=True)),
+ ('size', models.PositiveIntegerField()),
+ ('status', models.CharField(max_length=15)),
+ ('from_domain', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sent_messages_log', to='admin.Domain')),
+ ('to_domain', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='recv_messages_log', to='admin.Domain')),
+ ],
+ options={
+ 'ordering': ['date'],
+ },
+ ),
+ ]
diff --git a/modoboa/maillog/migrations/0002_auto_20200916_1346.py b/modoboa/maillog/migrations/0002_auto_20200916_1346.py
new file mode 100644
index 000000000..7d6f6a821
--- /dev/null
+++ b/modoboa/maillog/migrations/0002_auto_20200916_1346.py
@@ -0,0 +1,36 @@
+# Generated by Django 2.2.12 on 2020-09-16 11:46
+
+from django.db import migrations
+
+
+def rename_settings(apps, schema_editor):
+ LocalConfig = apps.get_model("core", "LocalConfig")
+ lc = LocalConfig.objects.first()
+ if not lc:
+ return
+ stats_params = lc._parameters.pop("modoboa_stats", None)
+ if stats_params:
+ lc._parameters["maillog"] = stats_params
+ lc.save()
+
+
+def backwards(apps, schema_editor):
+ LocalConfig = apps.get_model("core", "LocalConfig")
+ lc = LocalConfig.objects.first()
+ if not lc:
+ return
+ params = lc._parameters.pop("maillog", None)
+ if params:
+ lc._parameters["modoboa_stats"] = params
+ lc.save()
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('maillog', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.RunPython(rename_settings, backwards),
+ ]
diff --git a/modoboa/maillog/migrations/__init__.py b/modoboa/maillog/migrations/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/modoboa/maillog/models.py b/modoboa/maillog/models.py
new file mode 100644
index 000000000..5f33d700e
--- /dev/null
+++ b/modoboa/maillog/models.py
@@ -0,0 +1,27 @@
+"""App. related models."""
+
+from django.db import models
+
+
+class Maillog(models.Model):
+ """A model to store message logs."""
+
+ queue_id = models.CharField(max_length=50)
+ date = models.DateTimeField()
+ sender = models.EmailField()
+ rcpt = models.EmailField()
+ original_rcpt = models.EmailField(null=True)
+ size = models.PositiveIntegerField()
+ status = models.CharField(max_length=15)
+
+ from_domain = models.ForeignKey(
+ "admin.Domain", on_delete=models.SET_NULL, null=True,
+ related_name="sent_messages_log"
+ )
+ to_domain = models.ForeignKey(
+ "admin.Domain", on_delete=models.SET_NULL, null=True,
+ related_name="recv_messages_log"
+ )
+
+ class Meta:
+ ordering = ['date']
diff --git a/modoboa/maillog/parser.py b/modoboa/maillog/parser.py
new file mode 100644
index 000000000..4541247ec
--- /dev/null
+++ b/modoboa/maillog/parser.py
@@ -0,0 +1,301 @@
+"""Parsing tools for maillog related files."""
+
+import io
+import re
+import time
+
+from modoboa.admin import models as admin_models
+from modoboa.lib.email_utils import split_mailbox
+
+from . import utils
+
+
+class MaillogParser:
+ """Main parser class for maillog file."""
+
+ def __init__(self, year=None, greylist=False, verbose=False, debug=False):
+ """Constructor."""
+ self.debug = debug
+ self.verbose = verbose
+
+ self.domains = []
+ self._load_domain_list()
+
+ self.workdict = {}
+ self.lupdates = {}
+
+ self.__year = year
+ curtime = time.localtime()
+ if not self.__year:
+ self.__year = curtime.tm_year
+ self.curmonth = curtime.tm_mon
+
+ # set up regular expression
+ self._date_expressions = [
+ r"(?P\w+)\s+(?P\d+)\s+(?P\d+):(?P\d+):(?P\d+)(?P.*)", # noqa
+ r"(?P\d+)-(?P\d+)-(?P\d+)T(?P\d+):(?P\d+):(?P\d+)\.\d+\+\d+:\d+(?P.*)" # noqa
+ ]
+ self._date_expressions = [
+ re.compile(v) for v in self._date_expressions]
+ self.date_expr = None
+ self._regex = {
+ "line": r"\s+([-\w\.]+)\s+(\w+)/?(\w*)[[](\d+)[]]:\s+(.*)",
+ "id": r"(\w+): (.*)",
+ "reject": r"reject: .*from=<.*>,? to=<[^@]+@([^>]+)>",
+ "message-id": r"message-id=<([^>]*)>",
+ "from+size": r"from=<([^>]*)>, size=(\d+)",
+ "to+status": r"to=<([^>]*)>.*status=(\S+)",
+ "orig_to": r"orig_to=<([^>]*)>.*",
+ }
+ self._regex = {k: re.compile(v) for k, v in self._regex.items()}
+ self._srs_regex = {
+ "detect_srs": "^SRS[01][+-=]",
+ "reverse_srs0": r"^SRS0[+-=]\S+=\S{2}=(\S+)=(\S+)\@\S+$",
+ "reverse_srs1": r"^SRS1[+-=]\S+=\S+==\S+=\S{2}=(\S+)=(\S+)\@\S+$"
+ }
+ self._srs_regex = {
+ k: re.compile(v, re.IGNORECASE) for k, v in self._srs_regex.items()
+ }
+
+ self.greylist = greylist
+ if greylist:
+ self._dprint("[settings] greylisting enabled")
+
+ def _load_domain_list(self):
+ """Load the list of allowed domains."""
+ for dom in admin_models.Domain.objects.all():
+ domname = str(dom.name)
+ self.domains += [domname]
+ # Also add alias domains
+ for alias in dom.domainalias_set.all():
+ aliasname = str(alias.name)
+ self.domains += [aliasname]
+
+ def _dprint(self, msg):
+ """Print a debug message if required.
+
+ :param str msg: debug message
+ """
+ if not self.debug:
+ return
+ print(msg)
+
+ def year(self, month):
+ """Return the appropriate year
+
+ Date used in log files don't always embark the year so we need
+ to guess it :p
+
+ This method tries to deal with year changes in a simply
+ (ugly?) way: if we currently are in january and the given
+ month is different, return the current year -1. Otherwise,
+ return the current year.
+
+ Obviously, this method only works for year to year + 1
+ changes.
+
+ :param month: the month of the current record beeing parsed
+ :return: an integer
+ """
+ month = (
+ time.strptime(month, "%b").tm_mon if not month.isdigit()
+ else int(month)
+ )
+ if self.curmonth == 1 and month != self.curmonth:
+ return self.__year - 1
+ return self.__year
+
+ def _store_current_date(self, match):
+ """Transform and store parsed date."""
+ ho = match.group("hour")
+ mi = match.group("min")
+ se = match.group("sec")
+ mo = match.group("month")
+ da = match.group("day")
+ try:
+ ye = match.group("year")
+ except IndexError:
+ ye = self.year(mo)
+ self.cur_t = utils.date_to_timestamp([ye, mo, da, ho, mi, se])
+
+ def _parse_date(self, line):
+ """Try to match a date inside :kw:`line` and to convert it to
+ a timestamp.
+
+ We try different date format until we find valid one. We then
+ store it for future use.
+
+ :param str line: a log entry
+ :return: the remaining part of the line or None
+ """
+ match = None
+ if self.date_expr is None:
+ for expr in self._date_expressions:
+ match = expr.match(line)
+ if match is not None:
+ self.date_expr = expr
+ break
+ else:
+ match = self.date_expr.match(line)
+ if match is None:
+ return None
+ self._store_current_date(match)
+ return match.group("eol")
+
+ def is_srs_forward(self, mail_address):
+ """ Check if mail address has been mangled by SRS
+
+ Sender Rewriting Scheme (SRS) modifies mail adresses so that they
+ always end in a local domain.
+
+ :param str mail_address
+ :return a boolean
+ """
+ return self._srs_regex["detect_srs"].match(mail_address) is not None
+
+ def reverse_srs(self, mail_address):
+ """ Try to unwind a mail address rewritten by SRS
+
+ Sender Rewriting Scheme (SRS) modifies mail adresses so that they
+ always end in a local domain. Common Postfix implementations of SRS
+ rewrite all non-local mail addresss.
+
+ :param str mail_address
+ :return a str
+ """
+ m = self._srs_regex["reverse_srs0"].match(mail_address)
+ m = self._srs_regex["reverse_srs1"].match(mail_address) \
+ if m is None else m
+
+ if m is not None:
+ return "%s@%s" % m.group(2, 1)
+ return mail_address
+
+ def new_domain_event(self, domain, name, size=None):
+ """Take action about new event for domain."""
+ pass
+
+ def new_message_processed(
+ self, queue_id, msg_status, from_domain, to_domain, msg_to,
+ msg_orig_to):
+ """Store new message in local database."""
+ pass
+
+ def _parse_postfix(self, log, host, pid, subprog):
+ """ Parse a log entry generated by Postfix.
+
+ :param str log: logged message
+ :param str host: hostname
+ :param str pid: process ID
+ :return: True on success
+ """
+ m = self._regex["id"].match(log)
+ if m is None:
+ return False
+
+ queue_id, msg = m.groups()
+
+ # Handle rejected mails.
+ if queue_id == "NOQUEUE":
+ m = self._regex["reject"].match(msg)
+ dom = m.group(1) if m is not None else None
+ if dom in self.domains:
+ condition = (
+ self.greylist and (
+ "Greylisted" in msg or
+ (subprog == "postscreen" and " 450 " in msg))
+ )
+ self.new_domain_event(
+ dom, "greylist" if condition else "reject")
+ return True
+
+ # Message acknowledged.
+ m = self._regex["message-id"].search(msg)
+ if m is not None:
+ self.workdict[queue_id] = {"from": m.group(1), "size": 0}
+ return True
+
+ # Message enqueued.
+ m = self._regex["from+size"].search(msg)
+ if m is not None:
+ self.workdict[queue_id] = {
+ "from": self.reverse_srs(m.group(1)),
+ "size": int(m.group(2))
+ }
+ return True
+
+ # Message disposition.
+ m = self._regex["to+status"].search(msg)
+ if m is None:
+ return False
+ (msg_to, msg_status) = m.groups()
+ if queue_id not in self.workdict:
+ self._dprint("[parser] inconsistent mail (%s: %s), skipping"
+ % (queue_id, msg_to))
+ return True
+
+ # orig_to is optional.
+ m = self._regex["orig_to"].search(msg)
+ msg_orig_to = m.group(1) if m is not None else None
+
+ # Handle local "from" domains.
+ from_domain = split_mailbox(self.workdict[queue_id]["from"])[1]
+ if from_domain is not None and from_domain in self.domains:
+ self.new_domain_event(
+ from_domain, "sent", self.workdict[queue_id]["size"])
+
+ # Handle local "to" domains.
+ to_domain = None
+ condition = (
+ msg_orig_to is not None and
+ not self.is_srs_forward(msg_orig_to)
+ )
+ if condition:
+ to_domain = split_mailbox(msg_orig_to)[1]
+ if to_domain is None:
+ to_domain = split_mailbox(msg_to)[1]
+
+ if msg_status == "sent":
+ msg_status = "recv"
+ self.new_domain_event(
+ to_domain, "recv", self.workdict[queue_id]["size"])
+
+ # Store log entry
+ self.new_message_processed(
+ queue_id, msg_status, from_domain, to_domain, msg_to, msg_orig_to)
+
+ return True
+
+ def _parse_line(self, line):
+ """Parse a single log line.
+
+ :param str line: log line
+ """
+ line = self._parse_date(line)
+ if line is None:
+ return
+ m = self._regex["line"].match(line)
+ if not m:
+ return
+ host, prog, subprog, pid, log = m.groups()
+
+ try:
+ parser = getattr(self, "_parse_{}".format(prog))
+ if not parser(log, host, pid, subprog):
+ self._dprint("[parser] ignoring %r log: %r" % (prog, log))
+ except AttributeError:
+ self._dprint(
+ "[parser] no log handler for \"%r\": %r".format(prog, log))
+
+ def parse(self, logfile):
+ """Process the log file."""
+ try:
+ fp = io.open(logfile, encoding="utf-8")
+ except IOError as errno:
+ self._dprint("%s" % errno)
+ return
+
+ for line in fp.readlines():
+ self._parse_line(line)
+
+ fp.close()
diff --git a/modoboa/maillog/signals.py b/modoboa/maillog/signals.py
new file mode 100644
index 000000000..59b2c7ae4
--- /dev/null
+++ b/modoboa/maillog/signals.py
@@ -0,0 +1,5 @@
+"""Modoboa stats signals."""
+
+import django.dispatch
+
+get_graph_sets = django.dispatch.Signal(providing_args=["user"])
diff --git a/modoboa/maillog/static/maillog/css/stats.css b/modoboa/maillog/static/maillog/css/stats.css
new file mode 100644
index 000000000..edd761be7
--- /dev/null
+++ b/modoboa/maillog/static/maillog/css/stats.css
@@ -0,0 +1,71 @@
+#menubar {
+ padding-left: 15px;
+}
+
+.chart {
+ position: relative;
+ margin-bottom: 20px;
+}
+
+.grid .tick {
+ stroke: darkgrey;
+ stroke-dasharray: 1, 2;
+ opacity: 0.9;
+}
+.grid path {
+ stroke-width: 0;
+}
+
+.line_over {
+ stroke: darkgrey;
+ stroke-width: 1.5px;
+ stroke-dasharray: 1, 2;
+ display: none;
+}
+
+.axis {
+ font-size: 0.7em;
+}
+
+.axis path,
+.axis line {
+ fill: none;
+ stroke: #000;
+ shape-rendering: crispEdges;
+}
+
+.area, .area2 {
+ fill-opacity: .6;
+}
+
+.brush .extent {
+ stroke: #fff;
+ fill-opacity: .125;
+ shape-rendering: crispEdges;
+}
+
+.legend rect {
+ fill: none;
+ stroke: black;
+ opacity: 0;
+}
+
+.tip {
+ position: absolute;
+ z-index: 20;
+ top: 60px;
+ display: none;
+ font-size: 0.9em;
+ padding: 5px;
+ background: rgba(0, 0, 0, 0.6);
+ color: #fff;
+ border-radius: 5px;
+}
+
+.tip p {
+ margin: 0;
+}
+
+.tracker {
+ display: none;
+}
\ No newline at end of file
diff --git a/modoboa/maillog/static/maillog/js/d3.legend.js b/modoboa/maillog/static/maillog/js/d3.legend.js
new file mode 100644
index 000000000..52b267a6c
--- /dev/null
+++ b/modoboa/maillog/static/maillog/js/d3.legend.js
@@ -0,0 +1,70 @@
+// d3.legend.js
+// (C) 2012 ziggy.jonsson.nyc@gmail.com
+// MIT licence
+
+(function() {
+ d3.legend = function(g, clicked) {
+ g.each(function() {
+ var g = d3.select(this),
+ items = {},
+ svg = d3.select(g.property("nearestViewportElement")),
+ legendPadding = g.attr("data-style-padding") || 5,
+ lb = g.selectAll(".legend-box").data([true]),
+ li = g.selectAll(".legend-items").data([true]);
+
+ lb.enter().append("rect").classed("legend-box", true);
+ li.enter().append("g").classed("legend-items", true);
+
+ svg.selectAll("[data-legend]").each(function() {
+ var self = d3.select(this);
+
+ items[self.attr("data-legend")] = {
+ pos: self.attr("data-legend-pos") || this.getBBox().y,
+ color: self.attr("data-legend-color") != undefined ? self.attr("data-legend-color") : self.style("fill") != 'none' ? self.style("fill") : self.style("stroke")
+ };
+ });
+
+ items = d3.entries(items).sort(function(a, b) {
+ return a.value.pos - b.value.pos;
+ });
+
+ li.selectAll("text")
+ .data(items, function(d) { return d.key; })
+ .call(function(d) { d.enter().append("text"); })
+ .call(function(d) { d.exit().remove(); })
+ .attr("y", function(d, i) { return i + "em"; })
+ .attr("x", "1em")
+ .text(function(d) { return d.key; });
+
+ li.selectAll("circle")
+ .data(items, function(d) { return d.key; })
+ .call(function(d) { d.enter().append("circle"); })
+ .call(function(d) { d.exit().remove(); })
+ .attr("cy", function(d, i) { return i - 0.25 + "em"; })
+ .attr("cx", 0)
+ .attr("r", "4px")
+ .style("stroke", function(d) { return d.value.color; })
+ .style("stroke-width", "1px")
+ .style("stroke-opacity", 0.8)
+ .style("fill", function(d) { return d.value.color; })
+ .on("click", function(d) {
+ var curve = d3.selectAll("[data-legend='" + d.key + "']");
+ var state = (curve.style("display") == "none")
+ ? "block" : "none";
+
+ curve.style("display", state);
+ d3.select(this).style(
+ "fill", (state == "none") ? "none" : d.value.color
+ );
+ });
+
+ // Reposition and resize the box
+ var lbbox = li[0][0].getBBox();
+ lb.attr("x", (lbbox.x - legendPadding))
+ .attr("y", (lbbox.y - legendPadding))
+ .attr("height", (lbbox.height + 2 * legendPadding))
+ .attr("width", (lbbox.width + 2 * legendPadding));
+ });
+ return g;
+ };
+})();
diff --git a/modoboa/maillog/static/maillog/js/graphic.js b/modoboa/maillog/static/maillog/js/graphic.js
new file mode 100644
index 000000000..1c422c0b8
--- /dev/null
+++ b/modoboa/maillog/static/maillog/js/graphic.js
@@ -0,0 +1,355 @@
+/**
+ * Creates an instance of ModoChart.
+ *
+ * @constructor
+ * @this {ModoChart}
+ * @param {string} selection add chart to this DOM element.
+ *
+ */
+function ModoChart(selection) {
+ var width = 0,
+ height = 300,
+ preview_height = 80,
+ brush_height = 7,
+ margin = {
+ left: 50,
+ top: 10,
+ bottom: 20
+ };
+ var container;
+
+ /**
+ * Create and render the chart.
+ *
+ * @this {ModoChart}
+ * @param {Object} data chart's data.
+ */
+ function my(data) {
+ container = d3.select(selection)
+ .append("div")
+ .attr("class", "chart");
+
+ container.append("h6").html(data.title);
+ width = $(selection).width();
+
+ var svg = container
+ .append("svg")
+ .attr("width", width)
+ .attr("height", my.totalHeight());
+
+ svg.append("defs").append("clipPath")
+ .attr("id", "clip")
+ .append("rect")
+ .attr("width", width - 2 * margin.left)
+ .attr("height", height + margin.top + margin.bottom);
+
+ var x = d3.time.scale().range([0, width - 2 * margin.left]),
+ x2 = d3.time.scale().range([0, width - 2 * margin.left]);
+ var xAxis = d3.svg.axis().scale(x).orient("bottom"),
+ xAxis2 = d3.svg.axis().scale(x2).orient("bottom");
+ var y = d3.scale.linear().range([height, 0]),
+ y2 = d3.scale.linear().range([preview_height, 0]);
+ var yAxis = d3.svg.axis().scale(y).orient("left");
+ var bisectDate = d3.bisector(function(d) { return d.x * 1000; }).left;
+
+ var brush = d3.svg.brush()
+ .x(x2)
+ .on("brush", brushed);
+
+ x.domain(
+ d3.extent(data.curves[0].data.map(function(d) { return d.x * 1000; }))
+ );
+ y.domain(
+ [d3.min(data.curves, function(d) { return d3.min(d.data, function(v) { return v.y; }); }),
+ d3.max(data.curves, function(d) { return d3.max(d.data, function(v) { return v.y; }); })
+ ]
+ );
+ x2.domain(x.domain());
+ y2.domain(y.domain());
+
+ var area = d3.svg.area()
+ .interpolate("monotone")
+ .x(function(d) { return x(d.x * 1000); })
+ .y0(height)
+ .y1(function(d) { return y(d.y); });
+ var area2 = d3.svg.area()
+ .interpolate("monotone")
+ .x(function(d) { return x2(d.x * 1000); })
+ .y0(preview_height)
+ .y1(function(d) { return y2(d.y); });
+
+ var focus = svg.append("g")
+ .attr("class", "focus")
+ .attr("width", width - 2 * margin.left)
+ .attr("height", height)
+ .attr("pointer-events", "all")
+ .attr("transform",
+ "translate(" + margin.left + "," + margin.top + ")");
+ var context = svg.append("g")
+ .attr("class", "context")
+ .attr("transform", "translate(" + margin.left + "," + (height + margin.top + margin.bottom + brush_height) + ")");
+
+ var curvegroup = focus.append("g")
+ .attr('clip-path', 'url(#clip)');
+ var curve = curvegroup.selectAll(".curve")
+ .data(data.curves)
+ .enter().append("g")
+ .attr("class", "curve");
+ var curve2 = context.selectAll(".curve")
+ .data(data.curves)
+ .enter().append("g")
+ .attr("class", "curve");
+
+ /* Vertical line */
+ var line = focus.append("line")
+ .attr("class", "line_over")
+ .attr("x1", 0)
+ .attr("y1", 0)
+ .attr("x2", 0)
+ .attr("y2", height);
+
+ /* Custom rectangle to catch mouse events */
+ var rect = focus.append("rect")
+ .attr("fill", "none")
+ .attr("width", width - 2 * margin.left)
+ .attr("height", height);
+
+ var tooltip = container
+ .append("div")
+ .attr("class", "remove")
+ .attr("class", "tip");
+
+ var points = [];
+
+ var path = curve.append("path")
+ .attr("class", "area")
+ .attr("d", function(d, i) {
+ var path = this;
+
+ points[path] = {};
+ d.data.forEach(function(d) {
+ points[path][d.x] = d.y;
+ });
+ focus.append("circle")
+ .attr("class", "tracker tracker-" + i)
+ .attr("r", 4);
+ return area(d.data);
+ })
+ .attr("data-legend", function(d) { return d.name; })
+ .attr("stroke", function(d) { return d.color; })
+ .attr("stroke-width", "2px")
+ .attr("stroke-opacity", 0.8)
+ .style("fill", function(d) { return d.color; });
+
+ var xrules = make_x_axis();
+
+ focus.append("g")
+ .attr("class", "grid")
+ .attr("transform", "translate(0," + height + ")")
+ .call(xrules);
+ focus.append("g")
+ .attr("class", "grid")
+ .call(make_y_axis);
+ focus.append("g")
+ .attr("class", "x axis")
+ .attr("transform", "translate(0," + height + ")")
+ .call(xAxis);
+ focus.append("g")
+ .attr("class", "y axis")
+ .call(yAxis);
+
+ rect.on("mouseover", function(d) {
+ line.style("display", "block");
+ tooltip.style("display", "block");
+ data.curves.forEach(function(d, cid) {
+ var display = d3.selectAll("[data-legend='" + d.name + "']")
+ .style("display");
+ if (display == "none") {
+ return;
+ }
+ focus.select(".tracker-" + cid).style("display", "block");
+ });
+ }).on("mousemove", function(d) {
+ var mousex = d3.mouse(this)[0],
+ mousey = d3.mouse(this)[1];
+ var date = x.invert(mousex);
+ var text = "";
+
+ line.attr("x1", mousex).attr("x2", mousex);
+ data.curves.forEach(function(d, cid) {
+ var display = d3.selectAll("[data-legend='" + d.name + "']")
+ .style("display");
+ if (display == "none") {
+ return;
+ }
+ var i = bisectDate(d.data, date.getTime(), 1),
+ d0 = d.data[i - 1],
+ d1 = d.data[i],
+ point = date.getTime() - (d0.x * 1000) > (d1.x * 1000) - date.getTime() ? d1 : d0;
+ text += "" + d.name + ": " + point.y.toFixed(3) + "
";
+
+ var tracker_x = x(point.x * 1000),
+ tracker_y = y(point.y);
+
+ focus.selectAll(".tracker-" + cid)
+ .style("fill", d.color)
+ .attr("cx", tracker_x)
+ .attr("cy", tracker_y);
+ });
+ if (mousex + tooltip[0][0].clientWidth >= width - 2 * margin.left) {
+ var left = mousex - (tooltip[0][0].clientWidth - (margin.left - 20));
+ tooltip.style("left", left + "px");
+ } else {
+ tooltip.style("left", (mousex + margin.left + 20) + "px");
+ }
+ tooltip.html("" + date + "
" + text);
+ }).on("mouseout", function(d) {
+ tooltip.style("display", "none");
+ line.style("display", "none");
+ focus.selectAll(".tracker").style("display", "none");
+ });
+
+ var legend = focus.append("g")
+ .attr("class", "legend")
+ .attr("transform", "translate(50,10)")
+ .style("font-size", "0.8em")
+ .call(d3.legend);
+
+ curve2.append("path")
+ .attr("class", "area2")
+ .attr("d", function(d) { return area2(d.data); })
+ .attr("stroke", function(d) { return d.color; })
+ .attr("stroke-width", "2px")
+ .attr("stroke-opacity", 0.8)
+ .attr("data-legend", function(d) { return d.name; })
+ .style("fill", function(d) { return d.color; });
+
+ context.append("g")
+ .attr("class", "x axis")
+ .attr("transform", "translate(0," + preview_height + ")")
+ .call(xAxis2);
+
+ context.append("g")
+ .attr("class", "grid")
+ .attr("transform", "translate(0," + preview_height + ")")
+ .call(make_preview_x_axis);
+
+ context.append("g")
+ .attr("class", "x brush")
+ .call(brush)
+ .selectAll("rect")
+ .attr("y", -6)
+ .attr("height", preview_height + brush_height);
+
+ function make_x_axis() {
+ return d3.svg.axis()
+ .scale(x)
+ .orient("bottom")
+ .ticks(6)
+ .tickSize(-height, 0, 0)
+ .tickFormat("");
+ }
+
+ function make_y_axis() {
+ return d3.svg.axis()
+ .scale(y)
+ .orient("left")
+ .ticks(6)
+ .tickSize(-(width - 2 * margin.left), 0, 0)
+ .tickFormat("");
+ }
+
+ function make_preview_x_axis() {
+ return d3.svg.axis()
+ .scale(x2)
+ .orient("bottom")
+ .ticks(6)
+ .tickSize(-preview_height, 0, 0)
+ .tickFormat("");
+ }
+
+ function brushed() {
+ x.domain(brush.empty() ? x2.domain() : brush.extent());
+ focus.selectAll(".area")
+ .attr("d", function(d) { return area(d.data); });
+ focus.select(".x.axis").call(xAxis);
+ focus.select(".grid").call(xrules);
+ }
+ }
+
+ /**
+ * Set or get chart's width.
+ *
+ * @this {ModoChart}
+ * @param {integer} value new width.
+ * @return {number} current width.
+ */
+ my.width = function(value) {
+ if (!arguments.length) return width;
+ width = value;
+ return my;
+ };
+
+ /**
+ * Set or get chart's height.
+ *
+ * @this {ModoChart}
+ * @param {integer} value new height.
+ * @return {number} current height.
+ */
+ my.height = function(value) {
+ if (!arguments.length) return height;
+ height = value;
+ return my;
+ };
+
+ /**
+ * Set or get chart's margin.
+ *
+ * @this {ModoChart}
+ * @param {Object} value new margin.
+ * @return {Object} current margin.
+ */
+ my.margin = function(value) {
+ if (!arguments.length) return margin;
+ margin = value;
+ return my;
+ };
+
+ /**
+ * Compute and return chart's total height.
+ *
+ * @this {ModoChart}
+ * @return {integer} total height.
+ */
+ my.totalHeight = function() {
+ return height + 2 * margin.top
+ + 2 * margin.bottom + preview_height
+ + brush_height;
+ };
+
+ /**
+ * Update the chart with new data.
+ *
+ * @this {ModoChart}
+ * @param {Object} data new data
+ */
+ my.update = function(data) {
+ container.select("svg").remove();
+ $(container[0][0]).empty();
+ my(data);
+ };
+
+ /**
+ * Hide the chart.
+ *
+ * @this {ModoChart}
+ */
+ my.hide = function () {
+ container.select("svg").remove();
+ $(container[0][0]).empty();
+ };
+
+
+ return my;
+}
diff --git a/modoboa/maillog/static/maillog/js/stats.js b/modoboa/maillog/static/maillog/js/stats.js
new file mode 100644
index 000000000..a62736cf6
--- /dev/null
+++ b/modoboa/maillog/static/maillog/js/stats.js
@@ -0,0 +1,189 @@
+/**
+ * Create an instance of Stats.
+ *
+ * @constructor
+ */
+var Stats = function(options) {
+ this.initialize(options);
+};
+
+Stats.prototype = {
+ constructor: Stats,
+
+ defaults: {
+ deflocation: "graphs/",
+ language: "en",
+ domain_list_url: null
+ },
+
+ initialize: function(options) {
+ this.options = $.extend({}, this.defaults, options);
+ this.options.defcallback = $.proxy(this.charts_cb, this);
+
+ var navobj = new History(this.options);
+ this.navobj = navobj;
+
+ this.charts = {};
+
+ if (navobj.params.searchquery !== undefined) {
+ $("#searchquery").val(navobj.params.searchquery);
+ }
+ $("#searchquery").focus(function() {
+ var $this = $(this);
+ if ($this.val() === undefined) {
+ return;
+ }
+ $this.data("oldvalue", $this.val());
+ $this.val("");
+ }).blur(function() {
+ var $this = $(this);
+ if ($this.val() === "") {
+ if ($this.data("oldvalue")) {
+ $this.val($this.data('oldvalue'));
+ $this.data("oldvalue", null);
+ } else {
+ $this.val(gettext("Search a domain"));
+ }
+ }
+ });
+ if (navobj.params.start) {
+ $("#id_from").val(navobj.params.start);
+ }
+ if (navobj.params.end) {
+ $("#id_to").val(navobj.params.end);
+ }
+ if (navobj.params.period) {
+ $("input[data-period={0}]".format(navobj.params.period))
+ .attr("checked", true)
+ .parent().addClass("active");
+ if (navobj.params.period == "custom") {
+ $("#custom_period").removeClass("hidden");
+ }
+ }
+ $("#custom-period .datetime_picker").datetimepicker({
+ format: 'YYYY-MM-DD HH:mm:ss',
+ locale: this.options.language,
+ ignoreReadonly: true
+ });
+ $("#searchquery").autocompleter({
+ choices: $.proxy(this.get_domain_list, this),
+ choice_selected: $.proxy(this.search_domain, this),
+ empty_choice: $.proxy(this.reset_search, this)
+ });
+
+ this.register_nav_callbacks();
+ this.listen();
+ },
+
+ listen: function() {
+ $(".period_selector").click($.proxy(this.change_period, this));
+ $("#customsend").on("click", $.proxy(this.custom_period, this));
+ $(window).resize($.proxy(this.resize_charts, this));
+ },
+
+ register_nav_callbacks: function() {
+ this.navobj.register_callback("graphs",
+ $.proxy(this.charts_cb, this));
+ },
+
+ /**
+ * Retrieve a list of domain from the server.
+ */
+ get_domain_list: function() {
+ var result;
+
+ $.ajax({
+ url: this.options.domain_list_url,
+ dataType: "json",
+ async: false
+ }).done(function(data) {
+ result = data;
+ });
+ return result;
+ },
+
+ change_period: function(e) {
+ e.preventDefault();
+ var $link = $(e.target).children("input");
+ var period = $link.attr("data-period");
+
+ if (period != "custom") {
+ $("#custom_period").addClass("hidden");
+ this.navobj.delparam("start").delparam("end");
+ this.navobj.setparam("period", $link.attr("data-period")).update();
+ } else {
+ $("#custom_period").removeClass("hidden");
+ }
+ },
+
+ /**
+ * Update all charts on resize event.
+ *
+ * @this {Stats}
+ */
+ resize_charts: function() {
+ var data = this.data;
+ $.each(this.charts, function(id, mychart) {
+ mychart.update(data.graphs[id]);
+ });
+ },
+
+ /**
+ * Create or update charts.
+ *
+ * @this {Stats}
+ * @param {Object} data
+ */
+ charts_cb: function(data) {
+ var menuid = "menu_" + this.navobj.getparam("gset");
+ var activeCharts = new Array();
+
+ $(".nav-sidebar > li").removeClass("active");
+ $("#" + menuid).addClass("active");
+ this.data = data;
+ if (!this.data.domain_selector) {
+ $("#domain-selector").hide();
+ } else {
+ $("#domain-selector").show();
+ }
+ $.each(data.graphs, $.proxy(function(id, graphdef) {
+ if (this.charts.hasOwnProperty(id)) {
+ this.charts[id].update(graphdef);
+ } else if (graphdef.curves.length) {
+ var mychart = ModoChart("#gset");
+ this.charts[id] = mychart;
+ mychart(graphdef);
+ }
+ activeCharts.push(id);
+ }, this));
+ $.each(this.charts, function (id, mychart) {
+ if (activeCharts.indexOf(id) === -1) {
+ mychart.hide();
+ }
+ });
+ },
+
+ search_domain: function(value) {
+ this.navobj.setparam("searchquery", value).update();
+ },
+
+ reset_search: function() {
+ $("#searchquery").data("oldvalue", null);
+ this.navobj.delparam("searchquery").update();
+ },
+
+ custom_period: function(e) {
+ e.preventDefault();
+ var $fromdate = $("#id_from");
+ var $todate = $("#id_to");
+
+ if ($fromdate.val() === "" || $todate.val() === "") {
+ return;
+ }
+ this.navobj.setparams({
+ period: "custom",
+ start: $fromdate.val(),
+ end: $todate.val()
+ }).update();
+ }
+};
diff --git a/modoboa/maillog/templates/maillog/index.html b/modoboa/maillog/templates/maillog/index.html
new file mode 100644
index 000000000..036ef3d32
--- /dev/null
+++ b/modoboa/maillog/templates/maillog/index.html
@@ -0,0 +1,91 @@
+{% extends "twocols.html" %}
+{% load i18n static %}
+
+{% block pagetitle %}{% trans "Graphical statistics" %}{% endblock %}
+
+{% block extra_css %}
+
+{% endblock %}
+
+{% block extra_js %}
+
+
+
+
+
+
+
+{% endblock %}
+
+{% block leftcol %}
+
+{% endblock %}
+
+{% block apparea %}
+
+
+
+
+
+ {% for id, gset in graph_sets.items %}
+
+ {% endfor %}
+
+
+{% endblock %}
diff --git a/modoboa/maillog/tests/__init__.py b/modoboa/maillog/tests/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/modoboa/maillog/tests/mail.log b/modoboa/maillog/tests/mail.log
new file mode 100644
index 000000000..f8fec0474
--- /dev/null
+++ b/modoboa/maillog/tests/mail.log
@@ -0,0 +1,519 @@
+%(day)s 11:00:20 server postfix/cleanup[28945]: A0320E00C3: message-id=
+%(day)s 11:00:20 server postfix/qmgr[26163]: A0320E00C3: from=, size=142646, nrcpt=1 (queue active)
+%(day)s 11:00:20 server postfix/smtpd[28944]: disconnect from localhost.localdomain[127.0.0.1] ehlo=1 xforward=1 mail=1 rcpt=1 data=1 quit=1 commands=6
+%(day)s 11:00:20 server amavis[14540]: (14540-09) Passed CLEAN {RelayedInbound}, [103.28.42.139]:56540 [103.28.42.139] -> , Message-ID: , Hits: -1.481, size: 142067, queued_as: A0320E00C3, dkim_sd=cm:mailing.agenda.be, 10172 ms
+%(day)s 11:00:20 server postfix/smtpd[28914]: proxy-accept: END-OF-MESSAGE: 250 2.0.0 from MTA(smtp:[127.0.0.1]:10025): 250 2.0.0 Ok: queued as A0320E00C3; from= to= proto=ESMTP helo=
+%(day)s 11:00:20 server dovecot: lmtp(30301): Connect from local
+%(day)s 11:00:20 server postfix/smtpd[28914]: disconnect from mx139.g.outbound.createsend.com[103.28.42.139] ehlo=1 mail=1 rcpt=1 data=1 quit=1 commands=5
+%(day)s 11:00:20 server dovecot: lmtp(user@test.com): z5FOLrRhDVpddgAABvoInA: sieve: msgid=: stored mail into mailbox 'notifications'
+%(day)s 11:00:20 server postfix/lmtp[28946]: A0320E00C3: to=, relay=mail.koalabs.org[private/dovecot-lmtp], delay=0.32, delays=0.11/0.01/0.01/0.2, dsn=2.0.0, status=sent (250 2.0.0 z5FOLrRhDVpddgAABvoInA Saved)
+%(day)s 11:00:20 server dovecot: lmtp(30301): Disconnect from local: Successful quit
+%(day)s 11:00:20 server postfix/qmgr[26163]: A0320E00C3: removed
+%(day)s 11:03:05 server postfix/postscreen[30620]: CONNECT from [207.154.235.157]:54810 to [94.23.45.181]:25
+%(day)s 11:03:11 server postfix/postscreen[30620]: PASS OLD [207.154.235.157]:54810
+%(day)s 11:03:12 server postfix/smtpd[30624]: connect from misas5.babino.pl[207.154.235.157]
+%(day)s 11:03:12 server postfix/smtpd[30624]: warning: 157.235.154.207.zen.spamhaus.org: RBL lookup error: Host or domain name not found. Name service error for name=157.235.154.207.zen.spamhaus.org type=A: Host not found, try again
+%(day)s 11:03:12 server postfix/cleanup[30637]: 2847EE008E: message-id=<20171116100312.2847EE008E@mail.koalabs.org>
+%(day)s 11:03:12 server postfix/qmgr[26163]: 2847EE008E: from=, size=271, nrcpt=1 (queue active)
+%(day)s 11:03:12 server policyd-spf[30640]: prepend Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=207.154.235.157; helo=misas5.babino.pl; envelope-from=zwroty@babino.pl; receiver=
+%(day)s 11:03:12 server postfix/smtpd[30624]: NOQUEUE: client=misas5.babino.pl[207.154.235.157]
+%(day)s 11:03:15 server postfix/smtpd[30647]: connect from localhost.localdomain[127.0.0.1]
+%(day)s 11:03:15 server postfix/smtpd[30647]: 1FEB7E00C4: client=localhost.localdomain[127.0.0.1], orig_client=misas5.babino.pl[207.154.235.157]
+%(day)s 11:03:15 server postfix/cleanup[30637]: 1FEB7E00C4: message-id=
+%(day)s 11:03:15 server postfix/qmgr[26163]: 1FEB7E00C4: from=, size=53549, nrcpt=1 (queue active)
+%(day)s 11:03:15 server postfix/smtpd[30647]: disconnect from localhost.localdomain[127.0.0.1] ehlo=1 xforward=1 mail=1 rcpt=1 data=1 quit=1 commands=6
+%(day)s 11:03:15 server amavis[19749]: (19749-18) Passed SPAM {RelayedTaggedInbound}, [207.154.235.157]:54810 [207.154.235.157] -> , Message-ID: , Hits: 6.89, size: 52477, queued_as: 1FEB7E00C4, dkim_sd=1509955623.babino:babino.pl, 2365 ms
+%(day)s 11:03:15 server postfix/smtpd[30624]: proxy-accept: END-OF-MESSAGE: 250 2.0.0 from MTA(smtp:[127.0.0.1]:10025): 250 2.0.0 Ok: queued as 1FEB7E00C4; from= to= proto=ESMTP helo=
+%(day)s 11:03:15 server postfix/smtpd[30624]: disconnect from misas5.babino.pl[207.154.235.157] ehlo=1 mail=1 rcpt=1 data=1 quit=1 commands=5
+%(day)s 11:03:18 server postfix/smtp[30639]: Anonymous TLS connection established to mail.domain.test[94.23.191.34]:25: TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)
+%(day)s 11:03:18 server postfix/smtp[30639]: 2847EE008E: to=, relay=mail.domain.test[94.23.191.34]:25, delay=6.3, delays=0.04/0.01/6/0.19, dsn=2.1.5, status=deliverable (250 2.1.5 Ok)
+%(day)s 11:03:18 server postfix/qmgr[26163]: 2847EE008E: removed
+%(day)s 11:03:21 server postfix/smtp[30648]: Anonymous TLS connection established to mail.domain.test[94.23.191.34]:25: TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)
+%(day)s 11:03:26 server postfix/smtp[30648]: 1FEB7E00C4: to=, relay=mail.domain.test[94.23.191.34]:25, delay=11, delays=0.05/0.01/6.1/5.2, dsn=2.0.0, status=sent (250 2.0.0 from MTA(smtp:[127.0.0.1]:10025): 250 2.0.0 Ok: queued as 5DDDA1F30002)
+%(day)s 11:03:26 server postfix/qmgr[26163]: 1FEB7E00C4: removed
+%(day)s 11:04:01 server dovecot: imap-login: Login: user=, method=PLAIN, rip=127.0.0.1, lip=127.0.0.1, mpid=30726, secured, session=
+%(day)s 11:04:01 server dovecot: imap(anomalie@test2.com): Logged out in=86 out=892
+%(day)s 11:04:01 server dovecot: imap-login: Login: user=, method=PLAIN, rip=127.0.0.1, lip=127.0.0.1, mpid=30742, secured, session=<2fBIvRZeMcd/AAAB>
+%(day)s 11:04:01 server dovecot: imap(evolution@test2.com): Logged out in=86 out=891
+%(day)s 11:04:01 server dovecot: imap-login: Login: user=, method=PLAIN, rip=127.0.0.1, lip=127.0.0.1, mpid=30754, secured, session=
+%(day)s 11:04:01 server dovecot: imap(question@test2.com): Logged out in=86 out=891
+%(day)s 11:04:15 server dovecot: imap-login: Login: user=, method=PLAIN, rip=127.0.0.1, lip=127.0.0.1, mpid=30828, secured, session=
+%(day)s 11:04:18 server dovecot: imap-login: Login: user=, method=PLAIN, rip=127.0.0.1, lip=127.0.0.1, mpid=30866, secured, session=
+%(day)s 11:04:32 server postfix/postscreen[30620]: CONNECT from [127.0.0.1]:53903 to [127.0.0.1]:25
+%(day)s 11:04:32 server postfix/postscreen[30620]: WHITELISTED [127.0.0.1]:53903
+%(day)s 11:04:32 server postfix/smtpd[30624]: connect from localhost.localdomain[127.0.0.1]
+%(day)s 11:04:32 server opendmarc[4245]: ignoring connection from localhost.localdomain
+%(day)s 11:04:32 server postfix/smtpd[30624]: NOQUEUE: client=localhost.localdomain[127.0.0.1]
+%(day)s 11:04:32 server postfix/smtpd[30647]: connect from localhost.localdomain[127.0.0.1]
+%(day)s 11:04:32 server postfix/smtpd[30647]: 8D1B3E008E: client=localhost.localdomain[127.0.0.1], orig_client=localhost.localdomain[127.0.0.1]
+%(day)s 11:04:32 server postfix/cleanup[30637]: 8D1B3E008E: message-id=<20171116100432.10564.83744@server.ngyn.org>
+%(day)s 11:04:32 server postfix/smtpd[30647]: disconnect from localhost.localdomain[127.0.0.1] ehlo=1 xforward=1 mail=1 rcpt=1 data=1 quit=1 commands=6
+%(day)s 11:04:32 server postfix/qmgr[26163]: 8D1B3E008E: from=, size=18506, nrcpt=1 (queue active)
+%(day)s 11:04:32 server amavis[21816]: (21816-19) Passed CLEAN {RelayedInternal}, MYNETS LOCAL [127.0.0.1]:53903 -> , Message-ID: <20171116100432.10564.83744@server.ngyn.org>, Hits: -, size: 18043, queued_as: 8D1B3E008E, 102 ms
+%(day)s 11:04:32 server postfix/smtpd[30624]: proxy-accept: END-OF-MESSAGE: 250 2.0.0 from MTA(smtp:[127.0.0.1]:10025): 250 2.0.0 Ok: queued as 8D1B3E008E; from= to= proto=ESMTP helo=
+%(day)s 11:04:32 server postfix/smtpd[30624]: disconnect from localhost.localdomain[127.0.0.1] ehlo=1 mail=1 rcpt=1 data=1 quit=1 commands=5
+%(day)s 11:04:32 server dovecot: lmtp(30933): Connect from local
+%(day)s 11:04:32 server postfix/postscreen[30620]: CONNECT from [127.0.0.1]:53914 to [127.0.0.1]:25
+%(day)s 11:04:32 server postfix/postscreen[30620]: WHITELISTED [127.0.0.1]:53914
+%(day)s 11:04:32 server postfix/smtpd[30624]: connect from localhost.localdomain[127.0.0.1]
+%(day)s 11:04:32 server opendmarc[4245]: ignoring connection from localhost.localdomain
+%(day)s 11:04:32 server postfix/smtpd[30624]: NOQUEUE: client=localhost.localdomain[127.0.0.1]
+%(day)s 11:04:32 server postfix/smtpd[30647]: connect from localhost.localdomain[127.0.0.1]
+%(day)s 11:04:32 server postfix/smtpd[30647]: AA7FDE00C4: client=localhost.localdomain[127.0.0.1], orig_client=localhost.localdomain[127.0.0.1]
+%(day)s 11:04:32 server postfix/cleanup[30637]: AA7FDE00C4: message-id=<20171116100432.10564.58862@server.ngyn.org>
+%(day)s 11:04:32 server postfix/smtpd[30647]: disconnect from localhost.localdomain[127.0.0.1] ehlo=1 xforward=1 mail=1 rcpt=1 data=1 quit=1 commands=6
+%(day)s 11:04:32 server postfix/qmgr[26163]: AA7FDE00C4: from=, size=18506, nrcpt=1 (queue active)
+%(day)s 11:04:32 server amavis[5246]: (05246-16) Passed CLEAN {RelayedInternal}, MYNETS LOCAL [127.0.0.1]:53914 -> , Message-ID: <20171116100432.10564.58862@server.ngyn.org>, Hits: -, size: 18043, queued_as: AA7FDE00C4, 79 ms
+%(day)s 11:04:32 server postfix/smtpd[30624]: proxy-accept: END-OF-MESSAGE: 250 2.0.0 from MTA(smtp:[127.0.0.1]:10025): 250 2.0.0 Ok: queued as AA7FDE00C4; from= to= proto=ESMTP helo=
+%(day)s 11:04:32 server postfix/smtpd[30624]: disconnect from localhost.localdomain[127.0.0.1] ehlo=1 mail=1 rcpt=1 data=1 quit=1 commands=5
+%(day)s 11:04:32 server dovecot: lmtp(30937): Connect from local
+%(day)s 11:04:32 server dovecot: lmtp(admin@test.com): cqWOJrBiDVrVeAAABvoInA: sieve: msgid=<20171116100432.10564.83744@server.ngyn.org>: stored mail into mailbox 'Koalabs'
+%(day)s 11:04:32 server dovecot: lmtp(30933): Disconnect from local: Successful quit
+%(day)s 11:04:32 server postfix/lmtp[30932]: 8D1B3E008E: to=, relay=mail.koalabs.org[private/dovecot-lmtp], delay=0.26, delays=0.06/0.01/0.01/0.19, dsn=2.0.0, status=sent (250 2.0.0 cqWOJrBiDVrVeAAABvoInA Saved)
+%(day)s 11:04:32 server postfix/qmgr[26163]: 8D1B3E008E: removed
+%(day)s 11:04:32 server dovecot: lmtp(admin@test.com): ES02LbBiDVrZeAAABvoInA: sieve: msgid=<20171116100432.10564.58862@server.ngyn.org>: stored mail into mailbox 'Koalabs'
+%(day)s 11:04:32 server dovecot: lmtp(30937): Disconnect from local: Successful quit
+%(day)s 11:04:32 server postfix/lmtp[30936]: AA7FDE00C4: to=, relay=mail.koalabs.org[private/dovecot-lmtp], delay=0.22, delays=0.05/0/0.01/0.16, dsn=2.0.0, status=sent (250 2.0.0 ES02LbBiDVrZeAAABvoInA Saved)
+%(day)s 11:04:32 server postfix/qmgr[26163]: AA7FDE00C4: removed
+%(day)s 11:06:45 server postfix/postscreen[31171]: CONNECT from [185.5.53.87]:58092 to [94.23.45.181]:25
+%(day)s 11:06:45 server postfix/postscreen[31171]: PASS OLD [185.5.53.87]:58092
+%(day)s 11:06:45 server postfix/smtpd[31172]: connect from main.nope.org.es[185.5.53.87]
+%(day)s 11:06:45 server postfix/smtpd[31172]: Anonymous TLS connection established from main.nope.org.es[185.5.53.87]: TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)
+%(day)s 11:06:45 server postfix/smtpd[31172]: warning: 87.53.5.185.zen.spamhaus.org: RBL lookup error: Host or domain name not found. Name service error for name=87.53.5.185.zen.spamhaus.org type=A: Host not found, try again
+%(day)s 11:06:46 server policyd-spf[31184]: prepend Received-SPF: None (no SPF record) identity=no SPF record; client-ip=185.5.53.87; helo=main.nope.org.es; envelope-from=<>; receiver=
+%(day)s 11:06:46 server postfix/smtpd[31172]: NOQUEUE: client=main.nope.org.es[185.5.53.87]
+%(day)s 11:06:51 server postfix/smtpd[31201]: connect from localhost.localdomain[127.0.0.1]
+%(day)s 11:06:51 server postfix/smtpd[31201]: AA3E6E00C3: client=localhost.localdomain[127.0.0.1], orig_client=main.nope.org.es[185.5.53.87]
+%(day)s 11:06:51 server postfix/cleanup[31202]: AA3E6E00C3: message-id=<201711161006.vAGA6jpH019527@main.nope.org.es>
+%(day)s 11:06:51 server postfix/smtpd[31201]: disconnect from localhost.localdomain[127.0.0.1] ehlo=1 xforward=1 mail=1 rcpt=1 data=1 quit=1 commands=6
+%(day)s 11:06:51 server postfix/qmgr[26163]: AA3E6E00C3: from=<>, size=3194, nrcpt=1 (queue active)
+%(day)s 11:06:51 server amavis[14540]: (14540-10) Passed CLEAN {RelayedInbound}, [185.5.53.87]:58092 [185.5.53.87] <> -> , Message-ID: <201711161006.vAGA6jpH019527@main.nope.org.es>, Hits: -1.889, size: 2718, queued_as: AA3E6E00C3, 5178 ms
+%(day)s 11:06:51 server postfix/smtpd[31172]: proxy-accept: END-OF-MESSAGE: 250 2.0.0 from MTA(smtp:[127.0.0.1]:10025): 250 2.0.0 Ok: queued as AA3E6E00C3; from=<> to= proto=ESMTP helo=
+%(day)s 11:06:51 server dovecot: lmtp(31204): Connect from local
+%(day)s 11:06:51 server dovecot: lmtp(admin@test.com): wUc3LDtjDVrkeQAABvoInA: sieve: msgid=<201711161006.vAGA6jpH019527@main.nope.org.es>: not sending reject message to <>
+%(day)s 11:06:51 server postfix/lmtp[31203]: AA3E6E00C3: to=, orig_to=, relay=mail.koalabs.org[private/dovecot-lmtp], delay=0.07, delays=0.04/0.01/0.01/0.01, dsn=2.0.0, status=sent (250 2.0.0 wUc3LDtjDVrkeQAABvoInA Saved)
+%(day)s 11:06:51 server dovecot: lmtp(31204): Disconnect from local: Successful quit
+%(day)s 11:06:51 server postfix/qmgr[26163]: AA3E6E00C3: removed
+%(day)s 11:06:52 server policyd-spf[31184]: prepend Received-SPF: None (no SPF record) identity=no SPF record; client-ip=185.5.53.87; helo=main.nope.org.es; envelope-from=<>; receiver=
+%(day)s 11:06:52 server postfix/smtpd[31172]: NOQUEUE: client=main.nope.org.es[185.5.53.87]
+%(day)s 11:06:54 server postfix/smtpd[31201]: connect from localhost.localdomain[127.0.0.1]
+%(day)s 11:06:54 server postfix/smtpd[31201]: 94D70E00C3: client=localhost.localdomain[127.0.0.1], orig_client=main.nope.org.es[185.5.53.87]
+%(day)s 11:06:54 server postfix/cleanup[31202]: 94D70E00C3: message-id=<201711161006.vAGA6jpI019527@main.nope.org.es>
+%(day)s 11:06:54 server postfix/smtpd[31201]: disconnect from localhost.localdomain[127.0.0.1] ehlo=1 xforward=1 mail=1 rcpt=1 data=1 quit=1 commands=6
+%(day)s 11:06:54 server postfix/qmgr[26163]: 94D70E00C3: from=<>, size=2988, nrcpt=1 (queue active)
+%(day)s 11:06:54 server dovecot: lmtp(31204): Connect from local
+%(day)s 11:06:54 server dovecot: lmtp(admin@test.com): AQQ5Jz5jDVrkeQAABvoInA: sieve: msgid=<201711161006.vAGA6jpI019527@main.nope.org.es>: not sending reject message to <>
+%(day)s 11:06:54 server postfix/lmtp[31203]: 94D70E00C3: to=, orig_to=, relay=mail.koalabs.org[private/dovecot-lmtp], delay=0.05, delays=0.05/0/0/0, dsn=2.0.0, status=sent (250 2.0.0 AQQ5Jz5jDVrkeQAABvoInA Saved)
+%(day)s 11:06:54 server dovecot: lmtp(31204): Disconnect from local: Successful quit
+%(day)s 11:06:54 server postfix/qmgr[26163]: 94D70E00C3: removed
+%(day)s 11:06:54 server amavis[19749]: (19749-19) Passed CLEAN {RelayedInbound}, [185.5.53.87]:58092 [185.5.53.87] <> -> , Message-ID: <201711161006.vAGA6jpI019527@main.nope.org.es>, Hits: -1.889, size: 2512, queued_as: 94D70E00C3, 2262 ms
+%(day)s 11:06:54 server postfix/smtpd[31172]: proxy-accept: END-OF-MESSAGE: 250 2.0.0 from MTA(smtp:[127.0.0.1]:10025): 250 2.0.0 Ok: queued as 94D70E00C3; from=<> to= proto=ESMTP helo=
+%(day)s 11:06:54 server postfix/smtpd[31172]: disconnect from main.nope.org.es[185.5.53.87] ehlo=2 starttls=1 mail=2 rcpt=2 data=2 rset=1 quit=1 commands=11
+%(day)s 11:09:09 server postfix/anvil[28916]: statistics: max connection rate 1/60s for (smtpd:192.254.112.99) at %(day)s 10:59:09
+%(day)s 11:09:09 server postfix/anvil[28916]: statistics: max connection count 1 for (smtpd:192.254.112.99) at %(day)s 10:59:09
+%(day)s 11:09:09 server postfix/anvil[28916]: statistics: max cache size 3 at %(day)s 11:00:08
+%(day)s 11:10:02 server dovecot: imap-login: Login: user=, method=PLAIN, rip=127.0.0.1, lip=127.0.0.1, mpid=31600, secured, session=
+%(day)s 11:10:02 server dovecot: imap(anomalie@test2.com): Logged out in=86 out=892
+%(day)s 11:10:02 server dovecot: imap-login: Login: user=, method=PLAIN, rip=127.0.0.1, lip=127.0.0.1, mpid=31618, secured, session=
+%(day)s 11:10:02 server dovecot: imap(evolution@test2.com): Logged out in=86 out=891
+%(day)s 11:10:02 server dovecot: imap-login: Login: user=, method=PLAIN, rip=127.0.0.1, lip=127.0.0.1, mpid=31630, secured, session=
+%(day)s 11:10:02 server dovecot: imap(question@test2.com): Logged out in=86 out=891
+%(day)s 11:14:42 server postfix/postscreen[32096]: CONNECT from [51.15.3.241]:35169 to [94.23.45.181]:25
+%(day)s 11:14:42 server postfix/postscreen[32096]: PASS OLD [51.15.3.241]:35169
+%(day)s 11:14:42 server postfix/smtpd[32097]: warning: hostname 51-15-3-241.rev.poneytelecom.eu does not resolve to address 51.15.3.241: Name or service not known
+%(day)s 11:14:42 server postfix/smtpd[32097]: connect from unknown[51.15.3.241]
+%(day)s 11:14:42 server postfix/smtpd[32097]: Anonymous TLS connection established from unknown[51.15.3.241]: TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)
+%(day)s 11:14:42 server postfix/smtpd[32097]: warning: 241.3.15.51.zen.spamhaus.org: RBL lookup error: Host or domain name not found. Name service error for name=241.3.15.51.zen.spamhaus.org type=A: Host not found, try again
+%(day)s 11:14:43 server policyd-spf[32112]: prepend Received-SPF: None (mailfrom) identity=mailfrom; client-ip=51.15.3.241; helo=intra.logikap.com; envelope-from=gitlab@intra.logikap.com; receiver=
+%(day)s 11:14:43 server postfix/smtpd[32097]: NOQUEUE: client=unknown[51.15.3.241]
+%(day)s 11:14:45 server postfix/smtpd[32119]: connect from localhost.localdomain[127.0.0.1]
+%(day)s 11:14:45 server postfix/smtpd[32119]: 339B2E00C3: client=localhost.localdomain[127.0.0.1], orig_client=unknown[51.15.3.241]
+%(day)s 11:14:45 server postfix/cleanup[32120]: 339B2E00C3: message-id=<31819cf6c71e45f4569b94888ea90ead@gitlab.intra.logikap.com>
+%(day)s 11:14:45 server postfix/smtpd[32119]: disconnect from localhost.localdomain[127.0.0.1] ehlo=1 xforward=1 mail=1 rcpt=1 data=1 quit=1 commands=6
+%(day)s 11:14:45 server postfix/qmgr[26163]: 339B2E00C3: from=, size=3772, nrcpt=1 (queue active)
+%(day)s 11:14:45 server amavis[14540]: (14540-11) Passed CLEAN {RelayedInbound}, [51.15.3.241]:35169 [51.15.3.241] -> , Message-ID: <31819cf6c71e45f4569b94888ea90ead@gitlab.intra.logikap.com>, Hits: -1.105, size: 3307, queued_as: 339B2E00C3, 1891 ms
+%(day)s 11:14:45 server postfix/smtpd[32097]: proxy-accept: END-OF-MESSAGE: 250 2.0.0 from MTA(smtp:[127.0.0.1]:10025): 250 2.0.0 Ok: queued as 339B2E00C3; from= to= proto=ESMTP helo=
+%(day)s 11:14:45 server postfix/smtpd[32097]: disconnect from unknown[51.15.3.241] ehlo=2 starttls=1 mail=1 rcpt=1 data=1 quit=1 commands=7
+%(day)s 11:14:45 server dovecot: lmtp(32122): Connect from local
+%(day)s 11:14:45 server dovecot: lmtp(admin@test.com): +NuIEBVlDVp6fQAABvoInA: sieve: msgid=<31819cf6c71e45f4569b94888ea90ead@gitlab.intra.logikap.com>: stored mail into mailbox 'INBOX'
+%(day)s 11:14:45 server postfix/lmtp[32121]: 339B2E00C3: to=, relay=mail.koalabs.org[private/dovecot-lmtp], delay=0.23, delays=0.06/0.01/0.01/0.16, dsn=2.0.0, status=sent (250 2.0.0 +NuIEBVlDVp6fQAABvoInA Saved)
+%(day)s 11:14:45 server dovecot: lmtp(32122): Disconnect from local: Successful quit
+%(day)s 11:14:45 server postfix/qmgr[26163]: 339B2E00C3: removed
+%(day)s 11:16:01 server dovecot: imap(admin@test.com): Logged out in=3056 out=66597
+%(day)s 11:16:01 server dovecot: managesieve-login: Login: user=, method=PLAIN, rip=127.0.0.1, lip=127.0.0.1, mpid=32316, secured, session=
+%(day)s 11:16:01 server dovecot: managesieve(admin@test.com): Debug: Loading modules from directory: /usr/lib/dovecot/modules
+%(day)s 11:16:01 server dovecot: managesieve(admin@test.com): Debug: Module loaded: /usr/lib/dovecot/modules/lib10_quota_plugin.so
+%(day)s 11:16:01 server dovecot: managesieve(admin@test.com): Debug: Added userdb setting: plugin/quota_rule=*:bytes=2048M
+%(day)s 11:16:01 server dovecot: managesieve(admin@test.com): Debug: Effective uid=1010, gid=1010, home=/srv/vmail/ngyn.org/tonio
+%(day)s 11:16:01 server dovecot: managesieve(admin@test.com): Debug: Quota root: name=User quota backend=dict args=:proxy::quota
+%(day)s 11:16:01 server dovecot: managesieve(admin@test.com): Debug: Quota rule: root=User quota mailbox=* bytes=2147483648 messages=0
+%(day)s 11:16:01 server dovecot: managesieve(admin@test.com): Debug: Quota grace: root=User quota bytes=214748364 (10%%)
+%(day)s 11:16:01 server dovecot: managesieve(admin@test.com): Debug: dict quota: user=admin@test.com, uri=proxy::quota, noenforcing=0
+%(day)s 11:16:01 server dovecot: managesieve(admin@test.com): Debug: Namespace inbox: type=private, prefix=, sep=, inbox=yes, hidden=no, list=yes, subscriptions=yes location=maildir:~/.maildir
+%(day)s 11:16:01 server dovecot: managesieve(admin@test.com): Debug: maildir++: root=/srv/vmail/ngyn.org/tonio/.maildir, index=, indexpvt=, control=, inbox=/srv/vmail/ngyn.org/tonio/.maildir, alt=
+%(day)s 11:16:01 server dovecot: managesieve(admin@test.com): Debug: quota: quota_over_flag check: STORAGE ret=1 value=989542 limit=2097152
+%(day)s 11:16:01 server dovecot: managesieve(admin@test.com): Debug: quota: quota_over_flag check: MESSAGE ret=0 value=47722 limit=0
+%(day)s 11:16:01 server dovecot: managesieve(admin@test.com): Debug: quota: quota_over_flag=0((null)) vs currently overquota=0
+%(day)s 11:16:01 server dovecot: managesieve(admin@test.com): Debug: sieve: Pigeonhole version 0.4.16 (fed8554) initializing
+%(day)s 11:16:01 server dovecot: managesieve(admin@test.com): Debug: sieve: file storage: Using active Sieve script path: /srv/vmail/ngyn.org/tonio/.dovecot.sieve
+%(day)s 11:16:01 server dovecot: managesieve(admin@test.com): Debug: sieve: file storage: Using script storage path: /srv/vmail/ngyn.org/tonio/sieve
+%(day)s 11:16:01 server dovecot: managesieve(admin@test.com): Debug: sieve: file storage: Using permissions from /srv/vmail/ngyn.org/tonio/sieve: mode=0750 gid=-1
+%(day)s 11:16:01 server dovecot: managesieve(admin@test.com): Debug: sieve: file storage: Relative path to sieve storage in active link: sieve/
+%(day)s 11:16:01 server dovecot: managesieve(admin@test.com): Debug: sieve: file storage: sync: Synchronization active
+%(day)s 11:16:01 server dovecot: managesieve(admin@test.com): Disconnected: Logged out bytes=6/41
+%(day)s 11:16:01 server dovecot: imap-login: Login: user=, method=PLAIN, rip=127.0.0.1, lip=127.0.0.1, mpid=32327, secured, session=
+%(day)s 11:16:01 server dovecot: imap(anomalie@test2.com): Logged out in=86 out=892
+%(day)s 11:16:01 server dovecot: imap-login: Login: user=, method=PLAIN, rip=127.0.0.1, lip=127.0.0.1, mpid=32345, secured, session=<2d046BZe1sh/AAAB>
+%(day)s 11:16:01 server dovecot: imap(evolution@test2.com): Logged out in=86 out=891
+%(day)s 11:16:02 server dovecot: imap-login: Login: user=, method=PLAIN, rip=127.0.0.1, lip=127.0.0.1, mpid=32359, secured, session=
+%(day)s 11:16:02 server dovecot: imap(question@test2.com): Logged out in=86 out=891
+%(day)s 11:16:03 server postfix/postscreen[32096]: CONNECT from [81.92.113.114]:60750 to [94.23.45.181]:25
+%(day)s 11:16:03 server postfix/postscreen[32096]: PASS OLD [81.92.113.114]:60750
+%(day)s 11:16:03 server postfix/smtpd[32097]: connect from mta1.news.tarsus.fr[81.92.113.114]
+%(day)s 11:16:03 server postfix/smtpd[32097]: warning: 114.113.92.81.zen.spamhaus.org: RBL lookup error: Host or domain name not found. Name service error for name=114.113.92.81.zen.spamhaus.org type=A: Host not found, try again
+%(day)s 11:16:03 server policyd-spf[32112]: prepend Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=81.92.113.114; helo=mta1.news.tarsus.fr; envelope-from=marketing@info.opensourcesummit.paris; receiver=
+%(day)s 11:16:03 server postfix/smtpd[32097]: NOQUEUE: client=mta1.news.tarsus.fr[81.92.113.114]
+%(day)s 11:16:04 server postfix/smtpd[32119]: connect from localhost.localdomain[127.0.0.1]
+%(day)s 11:16:04 server postfix/smtpd[32119]: F39BCE00C3: client=localhost.localdomain[127.0.0.1], orig_client=mta1.news.tarsus.fr[81.92.113.114]
+%(day)s 11:16:04 server postfix/cleanup[32120]: F39BCE00C3: message-id=<2.1.4.1101006021.4688801.152618499@info.opensourcesummit.paris>
+%(day)s 11:16:05 server postfix/smtpd[32119]: disconnect from localhost.localdomain[127.0.0.1] ehlo=1 xforward=1 mail=1 rcpt=1 data=1 quit=1 commands=6
+%(day)s 11:16:05 server postfix/qmgr[26163]: F39BCE00C3: from=, size=55627, nrcpt=1 (queue active)
+%(day)s 11:16:05 server amavis[19749]: (19749-20) Passed CLEAN {RelayedInbound}, [81.92.113.114]:60750 [81.92.113.114] -> , Message-ID: <2.1.4.1101006021.4688801.152618499@info.opensourcesummit.paris>, Hits: 3.698, size: 54587, queued_as: F39BCE00C3, dkim_sd=emv:info.opensourcesummit.paris, 1310 ms
+%(day)s 11:16:05 server postfix/smtpd[32097]: proxy-accept: END-OF-MESSAGE: 250 2.0.0 from MTA(smtp:[127.0.0.1]:10025): 250 2.0.0 Ok: queued as F39BCE00C3; from= to= proto=ESMTP helo=
+%(day)s 11:16:05 server dovecot: lmtp(32392): Connect from local
+%(day)s 11:16:05 server postfix/smtpd[32097]: disconnect from mta1.news.tarsus.fr[81.92.113.114] ehlo=1 mail=1 rcpt=1 data=1 quit=1 commands=5
+%(day)s 11:16:05 server dovecot: lmtp(user@test.com): d8zmAmVlDVqIfgAABvoInA: sieve: msgid=<2.1.4.1101006021.4688801.152618499@info.opensourcesummit.paris>: stored mail into mailbox 'INBOX'
+%(day)s 11:16:05 server postfix/lmtp[32121]: F39BCE00C3: to=, relay=mail.koalabs.org[private/dovecot-lmtp], delay=0.23, delays=0.04/0.01/0.01/0.17, dsn=2.0.0, status=sent (250 2.0.0 d8zmAmVlDVqIfgAABvoInA Saved)
+%(day)s 11:16:05 server dovecot: lmtp(32392): Disconnect from local: Successful quit
+%(day)s 11:16:05 server postfix/qmgr[26163]: F39BCE00C3: removed
+%(day)s 11:16:45 server dovecot: pop3-login: Login: user=, method=PLAIN, rip=209.85.216.158, lip=94.23.45.181, mpid=32406, TLS, session=
+%(day)s 11:16:45 server dovecot: pop3(imhotep@koalabs.org): Disconnected: Logged out top=0/0, retr=0/0, del=0/141, size=8023766
+%(day)s 11:17:01 server postfix/postscreen[32096]: CONNECT from [13.111.20.45]:47742 to [94.23.45.181]:25
+%(day)s 11:17:01 server postfix/postscreen[32096]: WHITELISTED [13.111.20.45]:47742
+%(day)s 11:17:01 server postfix/smtpd[32097]: connect from mta4.email.tfl.gov.uk[13.111.20.45]
+%(day)s 11:17:02 server postfix/smtpd[32097]: Anonymous TLS connection established from mta4.email.tfl.gov.uk[13.111.20.45]: TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)
+%(day)s 11:17:02 server postfix/smtpd[32097]: warning: 45.20.111.13.zen.spamhaus.org: RBL lookup error: Host or domain name not found. Name service error for name=45.20.111.13.zen.spamhaus.org type=A: Host not found, try again
+%(day)s 11:17:03 server policyd-spf[32112]: prepend Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=13.111.20.45; helo=mta4.email.tfl.gov.uk; envelope-from=bounce-150_html-21457874-18921-100001609-123@bounce.email.tfl.gov.uk; receiver=
+%(day)s 11:17:03 server postfix/smtpd[32097]: NOQUEUE: client=mta4.email.tfl.gov.uk[13.111.20.45]
+%(day)s 11:17:06 server postfix/smtpd[32119]: connect from localhost.localdomain[127.0.0.1]
+%(day)s 11:17:06 server postfix/smtpd[32119]: 9E1C3E00C3: client=localhost.localdomain[127.0.0.1], orig_client=mta4.email.tfl.gov.uk[13.111.20.45]
+%(day)s 11:17:06 server postfix/cleanup[32120]: 9E1C3E00C3: message-id=<42e8ad52-02fb-45f5-a7fe-763c52409f6c@dfw1s10mta44.xt.local>
+%(day)s 11:17:08 server postfix/smtpd[32119]: disconnect from localhost.localdomain[127.0.0.1] ehlo=1 xforward=1 mail=1 rcpt=1 data=1 quit=1 commands=6
+%(day)s 11:17:08 server postfix/qmgr[26163]: 9E1C3E00C3: from=, size=71561, nrcpt=1 (queue active)
+%(day)s 11:17:08 server amavis[5246]: (05246-17) Passed CLEAN {RelayedInbound}, [13.111.20.45]:47742 [13.111.20.45] -> , Message-ID: <42e8ad52-02fb-45f5-a7fe-763c52409f6c@dfw1s10mta44.xt.local>, Hits: 2.492, size: 70304, queued_as: 9E1C3E00C3, dkim_sd=10dkim1:email.tfl.gov.uk, 3667 ms
+%(day)s 11:17:08 server postfix/smtpd[32097]: proxy-accept: END-OF-MESSAGE: 250 2.0.0 from MTA(smtp:[127.0.0.1]:10025): 250 2.0.0 Ok: queued as 9E1C3E00C3; from= to= proto=ESMTP helo=
+%(day)s 11:17:08 server dovecot: lmtp(32482): Connect from local
+%(day)s 11:17:08 server dovecot: lmtp(user@test.com): +IarBqRlDVrifgAABvoInA: sieve: msgid=<42e8ad52-02fb-45f5-a7fe-763c52409f6c@dfw1s10mta44.xt.local>: stored mail into mailbox 'INBOX'
+%(day)s 11:17:08 server postfix/lmtp[32121]: 9E1C3E00C3: to=, relay=mail.koalabs.org[private/dovecot-lmtp], delay=1.6, delays=1.5/0.01/0.01/0.17, dsn=2.0.0, status=sent (250 2.0.0 +IarBqRlDVrifgAABvoInA Saved)
+%(day)s 11:17:08 server dovecot: lmtp(32482): Disconnect from local: Successful quit
+%(day)s 11:17:08 server postfix/qmgr[26163]: 9E1C3E00C3: removed
+%(day)s 11:17:08 server postfix/smtpd[32097]: disconnect from mta4.email.tfl.gov.uk[13.111.20.45] ehlo=2 starttls=1 mail=1 rcpt=1 data=1 quit=1 commands=7
+%(day)s 11:17:27 server postfix/postscreen[32096]: CONNECT from [51.254.143.19]:34827 to [94.23.45.181]:25
+%(day)s 11:17:33 server postfix/postscreen[32096]: NOQUEUE: reject: RCPT from [51.254.143.19]:34827: 450 4.3.2 Service currently unavailable; from=, to=, proto=ESMTP, helo=
+%(day)s 11:17:33 server postfix/postscreen[32096]: PASS NEW [51.254.143.19]:34827
+%(day)s 11:17:33 server postfix/postscreen[32096]: DISCONNECT [51.254.143.19]:34827
+%(day)s 11:17:43 server postfix/postscreen[32096]: CONNECT from [172.110.9.131]:37348 to [94.23.45.181]:25
+%(day)s 11:17:43 server postfix/postscreen[32096]: PASS OLD [172.110.9.131]:37348
+%(day)s 11:17:43 server postfix/smtpd[32097]: connect from unknown[172.110.9.131]
+%(day)s 11:17:43 server postfix/smtpd[32097]: Anonymous TLS connection established from unknown[172.110.9.131]: TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)
+%(day)s 11:17:43 server postfix/smtpd[32097]: warning: 131.9.110.172.zen.spamhaus.org: RBL lookup error: Host or domain name not found. Name service error for name=131.9.110.172.zen.spamhaus.org type=A: Host not found, try again
+%(day)s 11:17:44 server policyd-spf[32112]: prepend Received-SPF: None (no SPF record) identity=no SPF record; client-ip=172.110.9.131; helo=mailstore.paxi.cf; envelope-from=<>; receiver=
+%(day)s 11:17:44 server postfix/smtpd[32097]: NOQUEUE: client=unknown[172.110.9.131]
+%(day)s 11:17:45 server postfix/smtpd[32119]: connect from localhost.localdomain[127.0.0.1]
+%(day)s 11:17:45 server postfix/smtpd[32119]: 34B20E00C3: client=localhost.localdomain[127.0.0.1], orig_client=unknown[172.110.9.131]
+%(day)s 11:17:45 server postfix/cleanup[32120]: 34B20E00C3: message-id=<20171116101742.C6A924DCECC6@mailstore.paxi.cf>
+%(day)s 11:17:45 server postfix/smtpd[32119]: disconnect from localhost.localdomain[127.0.0.1] ehlo=1 xforward=1 mail=1 rcpt=1 data=1 quit=1 commands=6
+%(day)s 11:17:45 server postfix/qmgr[26163]: 34B20E00C3: from=<>, size=4102, nrcpt=1 (queue active)
+%(day)s 11:17:45 server amavis[21816]: (21816-20) Passed CLEAN {RelayedInbound}, [172.110.9.131]:37348 [172.110.9.131] <> -> , Message-ID: <20171116101742.C6A924DCECC6@mailstore.paxi.cf>, Hits: -1.106, size: 3626, queued_as: 34B20E00C3, 1017 ms
+%(day)s 11:17:45 server dovecot: lmtp(32482): Connect from local
+%(day)s 11:17:45 server postfix/smtpd[32097]: proxy-accept: END-OF-MESSAGE: 250 2.0.0 from MTA(smtp:[127.0.0.1]:10025): 250 2.0.0 Ok: queued as 34B20E00C3; from=<> to= proto=ESMTP helo=
+%(day)s 11:17:45 server dovecot: lmtp(admin@test.com): WQg7D8llDVrifgAABvoInA: sieve: msgid=<20171116101742.C6A924DCECC6@mailstore.paxi.cf>: not sending reject message to <>
+%(day)s 11:17:45 server postfix/lmtp[32121]: 34B20E00C3: to=, orig_to=, relay=mail.koalabs.org[private/dovecot-lmtp], delay=0.05, delays=0.04/0.01/0/0, dsn=2.0.0, status=sent (250 2.0.0 WQg7D8llDVrifgAABvoInA Saved)
+%(day)s 11:17:45 server dovecot: lmtp(32482): Disconnect from local: Successful quit
+%(day)s 11:17:45 server postfix/qmgr[26163]: 34B20E00C3: removed
+%(day)s 11:17:45 server postfix/postscreen[32096]: CONNECT from [108.174.3.154]:54018 to [94.23.45.181]:25
+%(day)s 11:17:45 server postfix/postscreen[32096]: WHITELISTED [108.174.3.154]:54018
+%(day)s 11:17:45 server postfix/smtpd[32097]: disconnect from unknown[172.110.9.131] ehlo=2 starttls=1 mail=1 rcpt=1 data=1 quit=1 commands=7
+%(day)s 11:17:45 server postfix/smtpd[32502]: connect from mailb-bb.linkedin.com[108.174.3.154]
+%(day)s 11:17:45 server postfix/smtpd[32502]: Anonymous TLS connection established from mailb-bb.linkedin.com[108.174.3.154]: TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)
+%(day)s 11:17:46 server postfix/smtpd[32502]: warning: 154.3.174.108.zen.spamhaus.org: RBL lookup error: Host or domain name not found. Name service error for name=154.3.174.108.zen.spamhaus.org type=A: Host not found, try again
+%(day)s 11:17:46 server policyd-spf[32505]: prepend Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=108.174.3.154; helo=mailb-bb.linkedin.com; envelope-from=m-i58nvfj318qraba58ra9m2nttnwbk7s0evxwduql0g5d0pa6k36@bounce.linkedin.com; receiver=
+%(day)s 11:17:46 server postfix/smtpd[32502]: NOQUEUE: client=mailb-bb.linkedin.com[108.174.3.154]
+%(day)s 11:17:47 server postfix/postscreen[32096]: CONNECT from [81.92.113.115]:43100 to [94.23.45.181]:25
+%(day)s 11:17:47 server postfix/postscreen[32096]: PASS OLD [81.92.113.115]:43100
+%(day)s 11:17:47 server postfix/smtpd[32097]: connect from mta2.news.tarsus.fr[81.92.113.115]
+%(day)s 11:17:47 server postfix/smtpd[32097]: warning: 115.113.92.81.zen.spamhaus.org: RBL lookup error: Host or domain name not found. Name service error for name=115.113.92.81.zen.spamhaus.org type=A: Host not found, try again
+%(day)s 11:17:48 server policyd-spf[32112]: prepend Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=81.92.113.115; helo=mta2.news.tarsus.fr; envelope-from=marketing@info.opensourcesummit.paris; receiver=
+%(day)s 11:17:48 server postfix/smtpd[32097]: NOQUEUE: client=mta2.news.tarsus.fr[81.92.113.115]
+%(day)s 11:17:52 server postfix/smtpd[32119]: connect from localhost.localdomain[127.0.0.1]
+%(day)s 11:17:52 server postfix/smtpd[32119]: 69E9BE00C4: client=localhost.localdomain[127.0.0.1], orig_client=mailb-bb.linkedin.com[108.174.3.154]
+%(day)s 11:17:52 server postfix/cleanup[32120]: 69E9BE00C4: message-id=<205549081.609208.1510827464487.JavaMail.app@lor1-app2315.prod.linkedin.com>
+%(day)s 11:17:52 server postfix/smtpd[32119]: disconnect from localhost.localdomain[127.0.0.1] ehlo=1 xforward=1 mail=1 rcpt=1 data=1 quit=1 commands=6
+%(day)s 11:17:52 server postfix/qmgr[26163]: 69E9BE00C4: from=, size=35732, nrcpt=1 (queue active)
+%(day)s 11:17:52 server amavis[14540]: (14540-12) Passed CLEAN {RelayedInbound}, [108.174.3.154]:54018 [108.174.3.154] -> , Message-ID: <205549081.609208.1510827464487.JavaMail.app@lor1-app2315.prod.linkedin.com>, Hits: -7.019, size: 35066, queued_as: 69E9BE00C4, dkim_sd=proddkim1024:linkedin.com,proddkim1024:mailb.linkedin.com, 5477 ms
+%(day)s 11:17:52 server dovecot: lmtp(32482): Connect from local
+%(day)s 11:17:52 server postfix/smtpd[32502]: proxy-accept: END-OF-MESSAGE: 250 2.0.0 from MTA(smtp:[127.0.0.1]:10025): 250 2.0.0 Ok: queued as 69E9BE00C4; from= to= proto=ESMTP helo=
+%(day)s 11:17:52 server dovecot: lmtp(user@test.com): UdRqG9BlDVrifgAABvoInA: sieve: msgid=<205549081.609208.1510827464487.JavaMail.app@lor1-app2315.prod.linkedin.com>: stored mail into mailbox 'INBOX'
+%(day)s 11:17:52 server postfix/lmtp[32121]: 69E9BE00C4: to=, relay=mail.koalabs.org[private/dovecot-lmtp], delay=0.16, delays=0.03/0.01/0/0.13, dsn=2.0.0, status=sent (250 2.0.0 UdRqG9BlDVrifgAABvoInA Saved)
+%(day)s 11:17:52 server dovecot: lmtp(32482): Disconnect from local: Successful quit
+%(day)s 11:17:52 server postfix/qmgr[26163]: 69E9BE00C4: removed
+%(day)s 11:17:54 server postfix/smtpd[32119]: connect from localhost.localdomain[127.0.0.1]
+%(day)s 11:17:54 server postfix/smtpd[32119]: 0EC0CE00C4: client=localhost.localdomain[127.0.0.1], orig_client=mta2.news.tarsus.fr[81.92.113.115]
+%(day)s 11:17:54 server postfix/cleanup[32120]: 0EC0CE00C4: message-id=<2.1.4.1101006021.4688801.3909372752@info.opensourcesummit.paris>
+%(day)s 11:17:54 server postfix/qmgr[26163]: 0EC0CE00C4: from=, size=55600, nrcpt=1 (queue active)
+%(day)s 11:17:54 server amavis[32393]: (32393-01) Passed CLEAN {RelayedInbound}, [81.92.113.115]:43100 [81.92.113.115] -> , Message-ID: <2.1.4.1101006021.4688801.3909372752@info.opensourcesummit.paris>, Hits: 3.507, size: 54590, queued_as: 0EC0CE00C4, dkim_sd=emv:info.opensourcesummit.paris, 5899 ms
+%(day)s 11:17:54 server dovecot: lmtp(32482): Connect from local
+%(day)s 11:17:54 server postfix/smtpd[32097]: proxy-accept: END-OF-MESSAGE: 250 2.0.0 from MTA(smtp:[127.0.0.1]:10025): 250 2.0.0 Ok: queued as 0EC0CE00C4; from= to= proto=ESMTP helo=