├── .github ├── dependabot.yml └── workflows │ └── plugin.yml ├── .gitignore ├── .landscape.yaml ├── .tx └── config ├── LICENSE ├── MANIFEST.in ├── README.rst ├── modoboa_dmarc ├── __init__.py ├── admin.py ├── apps.py ├── constants.py ├── forms.py ├── handlers.py ├── lib.py ├── locale │ ├── br │ │ └── LC_MESSAGES │ │ │ └── django.po │ ├── cs_CZ │ │ └── LC_MESSAGES │ │ │ └── django.po │ ├── de │ │ └── LC_MESSAGES │ │ │ └── django.po │ ├── el_GR │ │ └── LC_MESSAGES │ │ │ └── django.po │ ├── en │ │ └── LC_MESSAGES │ │ │ └── django.po │ ├── fr │ │ └── LC_MESSAGES │ │ │ └── django.po │ ├── it │ │ └── LC_MESSAGES │ │ │ └── django.po │ ├── ja_JP │ │ └── LC_MESSAGES │ │ │ └── django.po │ ├── nl_NL │ │ └── LC_MESSAGES │ │ │ └── django.po │ ├── pt_BR │ │ └── LC_MESSAGES │ │ │ └── django.po │ └── sv │ │ └── LC_MESSAGES │ │ └── django.po ├── management │ ├── __init__.py │ └── commands │ │ ├── __init__.py │ │ └── import_aggregated_report.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_auto_20180124_2311.py │ ├── 0003_auto_20200227_0936.py │ └── __init__.py ├── models.py ├── modo_extension.py ├── templates │ └── modoboa_dmarc │ │ ├── _result_detail.html │ │ └── domain_report.html ├── templatetags │ ├── __init__.py │ └── dmarc_tags.py ├── tests │ ├── __init__.py │ ├── fail-reports │ │ └── Report_Domain_ngyn.org_Submitter_163.com_Report-ID_aggr_report_ngyn.org_20150705_163-failed.com.eml │ ├── mixins.py │ ├── reports │ │ ├── Report_Domain_ngyn.org_Submitter_163.com_Report-ID_aggr_report_ngyn.org_20150705_163.com.eml │ │ ├── Report_Domain_ngyn.org_Submitter_amazon.com_Report-ID_aggr_report_ngyn.org_20210607-ignored.eml │ │ ├── Report_Domain_ngyn.org_Submitter_amazon.com_Report-ID_aggr_report_ngyn.org_20210622.eml │ │ ├── Report_Domain_ngyn.org_Submitter_fastmail.com_Report-ID_2015.06.23.5672770.eml │ │ ├── Report_Domain_ngyn.org_Submitter_google.com_Report-ID_1282989064754998675.eml │ │ ├── Report_Domain_ngyn.org_Submitter_google.com_Report-ID_14872432101891615947.eml │ │ ├── Report_Domain_ngyn.org_Submitter_hotmail.com_Report-ID_66f317a021ff4cdabad2b350d3303615@hotmail.com.eml │ │ ├── Report_Domain_ngyn.org_Submitter_yahoo.com_Report-ID_1435111091.916236.eml │ │ └── Report_Domain_ngyn.org_Submitter_yahoo.com_Report-ID_1436111091.916236-ignored.eml │ ├── test_management_command.py │ └── test_views.py ├── urls.py └── views.py ├── requirements.txt ├── setup.cfg ├── setup.py ├── test-requirements.txt └── test_project ├── manage.py └── test_project ├── __init__.py ├── settings.py ├── urls.py └── wsgi.py /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: pip 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 10 8 | -------------------------------------------------------------------------------- /.github/workflows/plugin.yml: -------------------------------------------------------------------------------- 1 | name: Modoboa DMARC plugin 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | release: 9 | branches: [ master ] 10 | types: [ published ] 11 | 12 | env: 13 | POSTGRES_HOST: localhost 14 | 15 | jobs: 16 | test: 17 | runs-on: ubuntu-latest 18 | services: 19 | postgres: 20 | image: postgres:11 21 | env: 22 | POSTGRES_USER: postgres 23 | POSTGRES_PASSWORD: postgres 24 | POSTGRES_DB: postgres 25 | ports: 26 | # will assign a random free host port 27 | - 5432/tcp 28 | # needed because the postgres container does not provide a healthcheck 29 | options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 30 | mysql: 31 | image: mysql:8.0 32 | env: 33 | MYSQL_ROOT_PASSWORD: root 34 | MYSQL_USER: modoboa 35 | MYSQL_PASSWORD: modoboa 36 | MYSQL_DATABASE: modoboa 37 | ports: 38 | - 3306/tcp 39 | options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 40 | 41 | strategy: 42 | matrix: 43 | database: ['postgres', 'mysql'] 44 | python-version: [3.7, 3.8, 3.9] 45 | fail-fast: false 46 | 47 | steps: 48 | - uses: actions/checkout@v2 49 | - name: Set up Python ${{ matrix.python-version }} 50 | uses: actions/setup-python@v2 51 | with: 52 | python-version: ${{ matrix.python-version }} 53 | - name: Install dependencies 54 | run: | 55 | sudo apt-get update -y && sudo apt-get install -y librrd-dev rrdtool libmagic1 56 | python -m pip install --upgrade pip 57 | pip install -e git+https://github.com/modoboa/modoboa#egg=modoboa 58 | pip install -r requirements.txt 59 | pip install -r test-requirements.txt 60 | python setup.py develop 61 | - name: Install postgres requirements 62 | if: ${{ matrix.database == 'postgres' }} 63 | run: | 64 | pip install 'psycopg2-binary>=2.8,<2.9' 65 | pip install coverage 66 | echo "DB=postgres" >> $GITHUB_ENV 67 | - name: Install mysql requirements 68 | if: ${{ matrix.database == 'mysql' }} 69 | run: | 70 | pip install 'mysqlclient<2.0.4' 71 | echo "DB=mysql" >> $GITHUB_ENV 72 | - name: Test with pytest 73 | if: ${{ matrix.python-version != '3.9' || matrix.database != 'postgres' }} 74 | run: | 75 | cd test_project 76 | python3 manage.py test modoboa_dmarc 77 | env: 78 | # use localhost for the host here because we are running the job on the VM. 79 | # If we were running the job on in a container this would be postgres 80 | POSTGRES_PORT: ${{ job.services.postgres.ports[5432] }} # get randomly assigned published port 81 | MYSQL_HOST: 127.0.0.1 82 | MYSQL_PORT: ${{ job.services.mysql.ports[3306] }} # get randomly assigned published port 83 | MYSQL_USER: root 84 | 85 | - name: Test with pytest and coverage 86 | if: ${{ matrix.python-version == '3.9' && matrix.database == 'postgres' }} 87 | run: | 88 | cd test_project 89 | coverage run --source ../modoboa_dmarc manage.py test modoboa_dmarc 90 | env: 91 | # use localhost for the host here because we are running the job on the VM. 92 | # If we were running the job on in a container this would be postgres 93 | POSTGRES_PORT: ${{ job.services.postgres.ports[5432] }} # get randomly assigned published port 94 | MYSQL_HOST: 127.0.0.1 95 | MYSQL_PORT: ${{ job.services.mysql.ports[3306] }} # get randomly assigned published port 96 | MYSQL_USER: root 97 | - name: Upload coverage result 98 | if: ${{ matrix.python-version == '3.9' }} 99 | uses: actions/upload-artifact@v2 100 | with: 101 | name: coverage-results 102 | path: test_project/.coverage 103 | 104 | coverage: 105 | needs: test 106 | runs-on: ubuntu-latest 107 | steps: 108 | - uses: actions/checkout@v2 109 | - name: Set up Python 3.9 110 | uses: actions/setup-python@v2 111 | with: 112 | python-version: '3.9' 113 | - name: Install dependencies 114 | run: | 115 | pip install codecov 116 | - name: Download coverage results 117 | uses: actions/download-artifact@v2 118 | with: 119 | name: coverage-results 120 | - name: Report coverage 121 | run: | 122 | coverage report 123 | codecov 124 | 125 | release: 126 | needs: coverage 127 | runs-on: ubuntu-latest 128 | steps: 129 | - uses: actions/checkout@v2 130 | with: 131 | fetch-depth: 0 132 | - name: Set up Python 3.9 133 | uses: actions/setup-python@v2 134 | with: 135 | python-version: '3.9' 136 | - name: Build packages 137 | run: | 138 | sudo apt-get install librrd-dev rrdtool libssl-dev gettext libmagic1 139 | python -m pip install --upgrade pip setuptools wheel 140 | pip install -r requirements.txt 141 | cd modoboa_dmarc 142 | django-admin compilemessages 143 | cd .. 144 | python setup.py sdist bdist_wheel 145 | - name: Publish to Test PyPI 146 | if: endsWith(github.event.ref, '/master') 147 | uses: pypa/gh-action-pypi-publish@master 148 | with: 149 | user: __token__ 150 | password: ${{ secrets.test_pypi_password }} 151 | repository_url: https://test.pypi.org/legacy/ 152 | skip_existing: true 153 | - name: Publish distribution to PyPI 154 | if: startsWith(github.event.ref, 'refs/tags') || github.event_name == 'release' 155 | uses: pypa/gh-action-pypi-publish@master 156 | with: 157 | user: __token__ 158 | password: ${{ secrets.pypi_password }} 159 | skip_existing: true 160 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | *.egg-info/ 23 | .installed.cfg 24 | *.egg 25 | 26 | # PyInstaller 27 | # Usually these files are written by a python script from a template 28 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 29 | *.manifest 30 | *.spec 31 | 32 | # Installer logs 33 | pip-log.txt 34 | pip-delete-this-directory.txt 35 | 36 | # Unit test / coverage reports 37 | htmlcov/ 38 | .tox/ 39 | .coverage 40 | .coverage.* 41 | .cache 42 | nosetests.xml 43 | coverage.xml 44 | *,cover 45 | 46 | # Translations 47 | *.mo 48 | *.pot 49 | 50 | # Django stuff: 51 | *.log 52 | 53 | # Sphinx documentation 54 | docs/_build/ 55 | 56 | # PyBuilder 57 | target/ 58 | -------------------------------------------------------------------------------- /.landscape.yaml: -------------------------------------------------------------------------------- 1 | doc-warnings: yes 2 | test-warnings: no 3 | strictness: medium 4 | max-line-length: 80 5 | uses: 6 | - django 7 | autodetect: yes 8 | requirements: 9 | - requirements.txt 10 | ignore-paths: 11 | - docs 12 | ignore-patterns: 13 | - (^|/)migrations(/|$) 14 | -------------------------------------------------------------------------------- /.tx/config: -------------------------------------------------------------------------------- 1 | [main] 2 | host = https://www.transifex.com 3 | 4 | [modoboa.modoboa-dmarc-djangopo] 5 | file_filter = modoboa_dmarc/locale//LC_MESSAGES/django.po 6 | source_file = modoboa_dmarc/locale/en/LC_MESSAGES/django.po 7 | source_lang = en 8 | type = PO 9 | 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Modoboa 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.rst LICENSE requirements.txt 2 | recursive-include modoboa_dmarc *.html *.js *.css *.png *.po *.mo *.eml 3 | recursive-include doc * 4 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | modoboa-dmarc 2 | ============= 3 | 4 | |gha| |codecov| 5 | 6 | A set of tools to use DMARC through Modoboa. 7 | 8 | This plugin is still in BETA stage, for now it only parses XML aggregated 9 | reports and generate visual reports (using c3.js) on a per-domain basis. 10 | 11 | Installation 12 | ------------ 13 | Make sure to install the following additional system package according to your distribution: 14 | 15 | +-----------------+ 16 | | Debian / Ubuntu | 17 | +=================+ 18 | | libmagic1 | 19 | +-----------------+ 20 | 21 | +------------+ 22 | | CentOS | 23 | +============+ 24 | | file-devel | 25 | +------------+ 26 | 27 | Install this extension system-wide or inside a virtual environment by 28 | running the following command:: 29 | 30 | $ pip install modoboa-dmarc 31 | 32 | Edit the settings.py file of your modoboa instance and add 33 | ``modoboa_dmarc`` inside the ``MODOBOA_APPS`` variable like this:: 34 | 35 | MODOBOA_APPS = ( 36 | 'modoboa', 37 | 'modoboa.core', 38 | 'modoboa.lib', 39 | 'modoboa.admin', 40 | 'modoboa.limits', 41 | 'modoboa.relaydomains', 42 | 'modoboa.parameters', 43 | # Extensions here 44 | 'modoboa_dmarc', 45 | ) 46 | 47 | Run the following commands to setup the database tables:: 48 | 49 | $ cd 50 | $ python manage.py migrate modoboa_dmarc 51 | $ python manage.py collectstatic 52 | $ python manage.py load_initial_data 53 | 54 | Finally, restart the python process running modoboa (uwsgi, gunicorn, 55 | apache, whatever). 56 | 57 | Integration with Postfix 58 | ------------------------ 59 | 60 | A management command is provided to automatically parse DMARC 61 | aggregated reports (rua) and feed the database. The execution of this 62 | command can be automated with the definition of a postfix service and 63 | a custom transport table. 64 | 65 | First, declare a new service in ``/etc/postfix/master.cf``:: 66 | 67 | dmarc-rua-parser unix - n n - - pipe 68 | flags= user=vmail:vmail argv= /manage.py import_aggregated_report --pipe 69 | 70 | Define a new transport table inside ``/etc/postfix/main.cf``:: 71 | 72 | transport_maps = 73 | hash:/etc/postfix/dmarc_transport 74 | # other transport maps... 75 | 76 | Create a file called ``/etc/postfix/dmarc_transport`` with the following content:: 77 | 78 | dmarc-rua-parser: 79 | 80 | Hash the file using the following command:: 81 | 82 | $ postmap /etc/postfix/dmarc_transport 83 | 84 | Finally, reload postfix:: 85 | 86 | $ service postfix reload 87 | 88 | 89 | Specific Upgrade Instructions 90 | ----------------------------- 91 | 92 | 1.3.0 93 | ~~~~~ 94 | 95 | modoboa-dmarc now requires an additional system package according to your distribution: 96 | 97 | +-----------------+ 98 | | Debian / Ubuntu | 99 | +=================+ 100 | | libmagic1 | 101 | +-----------------+ 102 | 103 | +------------+ 104 | | CentOS | 105 | +============+ 106 | | file-devel | 107 | +------------+ 108 | 109 | 110 | .. |gha| image:: https://github.com/modoboa/modoboa-dmarc/actions/workflows/plugin.yml/badge.svg 111 | :target: https://github.com/modoboa/modoboa-dmarc/actions/workflows/plugin.yml 112 | 113 | .. |codecov| image:: https://codecov.io/gh/modoboa/modoboa-dmarc/branch/master/graph/badge.svg 114 | :target: https://codecov.io/gh/modoboa/modoboa-dmarc 115 | -------------------------------------------------------------------------------- /modoboa_dmarc/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """DMARC related tools for Modoboa.""" 4 | 5 | from __future__ import unicode_literals 6 | 7 | from pkg_resources import get_distribution, DistributionNotFound 8 | 9 | 10 | try: 11 | __version__ = get_distribution(__name__).version 12 | except DistributionNotFound: 13 | # package is not installed 14 | pass 15 | 16 | default_app_config = "modoboa_dmarc.apps.DmarcConfig" 17 | -------------------------------------------------------------------------------- /modoboa_dmarc/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /modoboa_dmarc/apps.py: -------------------------------------------------------------------------------- 1 | """AppConfig for dmarc.""" 2 | 3 | from django.apps import AppConfig 4 | 5 | 6 | class DmarcConfig(AppConfig): 7 | 8 | """App configuration.""" 9 | 10 | name = "modoboa_dmarc" 11 | verbose_name = "Modoboa DMARC tools" 12 | 13 | def ready(self): 14 | from . import handlers 15 | -------------------------------------------------------------------------------- /modoboa_dmarc/constants.py: -------------------------------------------------------------------------------- 1 | """DMARC constants.""" 2 | 3 | ALLOWED_REASON_TYPES = ( 4 | "forwarded", 5 | "sampled_out", 6 | "trusted_forwarder", 7 | "mailing_list", 8 | "local_policy", 9 | "other" 10 | ) 11 | -------------------------------------------------------------------------------- /modoboa_dmarc/forms.py: -------------------------------------------------------------------------------- 1 | """Custom forms.""" 2 | 3 | from django import forms 4 | from django.utils import timezone 5 | from django.utils.translation import ugettext_lazy as _ 6 | 7 | from modoboa.lib import form_utils 8 | from modoboa.parameters import forms as param_forms 9 | 10 | 11 | class ReportOptionsForm(forms.Form): 12 | """Report display options.""" 13 | 14 | current_year = forms.IntegerField(widget=forms.widgets.HiddenInput) 15 | current_week = forms.IntegerField() 16 | query = forms.ChoiceField( 17 | choices=[("previous", "Previous"), ("next", "Next")]) 18 | resolve_hostnames = forms.BooleanField( 19 | initial=False, required=False) 20 | 21 | def __init__(self, *args, **kwargs): 22 | """Constructor.""" 23 | super(ReportOptionsForm, self).__init__(*args, **kwargs) 24 | if not args: 25 | year, week, day = timezone.now().isocalendar() 26 | self.fields["current_year"].initial = year 27 | self.fields["current_week"].initial = week 28 | 29 | 30 | class ParametersForm(param_forms.AdminParametersForm): 31 | """Extension settings.""" 32 | 33 | app = "modoboa_dmarc" 34 | 35 | qsettings_sep = form_utils.SeparatorField(label=_("DNS settings")) 36 | 37 | enable_rlookups = form_utils.YesNoField( 38 | label=_("Enable reverse lookups"), 39 | initial=False, 40 | help_text=_( 41 | "Enable reverse DNS lookups (reports will be longer to display)" 42 | ) 43 | ) 44 | -------------------------------------------------------------------------------- /modoboa_dmarc/handlers.py: -------------------------------------------------------------------------------- 1 | """Django signal handlers for modoboa_dmarc.""" 2 | 3 | from django.urls import reverse 4 | from django.utils.translation import ugettext as _ 5 | from django.dispatch import receiver 6 | 7 | from modoboa.admin import signals as admin_signals 8 | 9 | from . import models 10 | 11 | 12 | @receiver(admin_signals.extra_domain_actions) 13 | def dmarc_domain_actions(sender, user, domain, **kwargs): 14 | """Return a link to access domain report.""" 15 | if not models.Record.objects.filter(header_from=domain).exists(): 16 | return [] 17 | return [{ 18 | "name": "dmarc_report", 19 | "url": reverse("modoboa_dmarc:domain_report", args=[domain.pk]), 20 | "title": _("Show DMARC report for {}").format(domain.name), 21 | "img": "fa fa-pie-chart" 22 | }] 23 | -------------------------------------------------------------------------------- /modoboa_dmarc/lib.py: -------------------------------------------------------------------------------- 1 | """Internal library.""" 2 | 3 | import datetime 4 | import email 5 | import fileinput 6 | import getpass 7 | import imaplib 8 | import zipfile 9 | import gzip 10 | import sys 11 | 12 | from defusedxml.ElementTree import fromstring 13 | import pytz.exceptions 14 | import six 15 | import magic 16 | 17 | from django.db import transaction 18 | from django.utils.encoding import smart_text 19 | from django.utils import timezone 20 | 21 | from modoboa.admin import models as admin_models 22 | 23 | from . import constants 24 | from . import models 25 | 26 | ZIP_CONTENT_TYPES = [ 27 | "application/x-zip-compressed", 28 | "application/x-zip", 29 | "application/zip", 30 | "application/gzip", 31 | "application/octet-stream", 32 | "text/xml", 33 | ] 34 | 35 | FILE_TYPES = [ 36 | "text/plain", 37 | "text/xml", 38 | ] 39 | 40 | 41 | def import_record(xml_node, report): 42 | """Import a record.""" 43 | record = models.Record(report=report) 44 | row = xml_node.find("row") 45 | record.source_ip = row.find("source_ip").text 46 | record.count = int(row.find("count").text) 47 | 48 | policy_evaluated = row.find("policy_evaluated") 49 | record.disposition = policy_evaluated.find("disposition").text 50 | record.dkim_result = policy_evaluated.find("dkim").text 51 | record.spf_result = policy_evaluated.find("spf").text 52 | reason = policy_evaluated.find("reason") 53 | if reason: 54 | record.reason_type = smart_text(reason.find("type").text)[:14] 55 | if record.reason_type not in constants.ALLOWED_REASON_TYPES: 56 | record.reason_type = "other" 57 | comment = reason.find("comment").text or "" 58 | record.reason_comment = comment 59 | 60 | identifiers = xml_node.find("identifiers") 61 | header_from = identifiers.find("header_from").text.split(".") 62 | domain = None 63 | while len(header_from) >= 2: 64 | domain = admin_models.Domain.objects.filter( 65 | name=".".join(header_from)).first() 66 | if domain is not None: 67 | record.header_from = domain 68 | break 69 | header_from = header_from[1:] 70 | if domain is None: 71 | print("Invalid record found (domain not local)") 72 | return None 73 | 74 | record.save() 75 | auth_results = xml_node.find("auth_results") 76 | for rtype in ["spf", "dkim"]: 77 | rnode = auth_results.find(rtype) 78 | if not rnode: 79 | continue 80 | models.Result.objects.create( 81 | record=record, type=rtype, domain=rnode.find("domain").text, 82 | result=rnode.find("result").text) 83 | 84 | 85 | @transaction.atomic 86 | def import_report(content): 87 | """Import an aggregated report.""" 88 | root = fromstring(content, forbid_dtd=True) 89 | metadata = root.find("report_metadata") 90 | print( 91 | "Importing report {} received from {}".format( 92 | metadata.find("report_id").text, 93 | metadata.find("org_name").text) 94 | ) 95 | reporter, created = models.Reporter.objects.get_or_create( 96 | email=metadata.find("email").text, 97 | defaults={"org_name": metadata.find("org_name").text} 98 | ) 99 | qs = models.Report.objects.filter( 100 | reporter=reporter, report_id=metadata.find("report_id").text) 101 | if qs.exists(): 102 | print("Report already imported.") 103 | return 104 | report = models.Report(reporter=reporter) 105 | 106 | report.report_id = metadata.find("report_id").text 107 | date_range = metadata.find("date_range") 108 | report.start_date = timezone.make_aware( 109 | datetime.datetime.fromtimestamp(int(date_range.find("begin").text)) 110 | ) 111 | report.end_date = timezone.make_aware( 112 | datetime.datetime.fromtimestamp(int(date_range.find("end").text)) 113 | ) 114 | 115 | policy_published = root.find("policy_published") 116 | for attr in ["domain", "adkim", "aspf", "p", "sp", "pct"]: 117 | node = policy_published.find(attr) 118 | if node is None or not node.text: 119 | if attr == "sp": 120 | node = fromstring('unstated', forbid_dtd=True) 121 | else: 122 | print(f"Report skipped because of malformed data (empty {attr})") 123 | return 124 | value = setattr(report, "policy_{}".format(attr), node.text) 125 | try: 126 | report.save() 127 | except (pytz.exceptions.AmbiguousTimeError): 128 | print("Report skipped because of invalid date.") 129 | return 130 | for record in root.findall("record"): 131 | import_record(record, report) 132 | 133 | 134 | def import_archive(archive, content_type=None): 135 | """Import reports contained inside (file pointer) 136 | - a zip archive, 137 | - a gzip file, 138 | - a xml file. 139 | """ 140 | if content_type == "text/xml": 141 | import_report(archive.read()) 142 | elif content_type in ["application/gzip", "application/octet-stream"]: 143 | with gzip.GzipFile(mode="r", fileobj=archive) as zfile: 144 | import_report(zfile.read()) 145 | else: 146 | with zipfile.ZipFile(archive, "r") as zfile: 147 | for fname in zfile.namelist(): 148 | import_report(zfile.read(fname)) 149 | 150 | 151 | def import_report_from_email(content): 152 | """Import a report from an email.""" 153 | if isinstance(content, six.string_types): 154 | msg = email.message_from_string(content) 155 | elif isinstance(content, six.binary_type): 156 | msg = email.message_from_bytes(content) 157 | else: 158 | msg = email.message_from_file(content) 159 | err = False 160 | for part in msg.walk(): 161 | if part.get_content_type() not in ZIP_CONTENT_TYPES: 162 | continue 163 | try: 164 | fpo = six.BytesIO(part.get_payload(decode=True)) 165 | # Try to get the actual file type of the buffer 166 | # required to make sure we are dealing with an XML file 167 | file_type = magic.Magic(uncompress=True, mime=True).from_buffer(fpo.read(2048)) 168 | fpo.seek(0) 169 | if file_type in FILE_TYPES: 170 | import_archive(fpo, content_type=part.get_content_type()) 171 | except (OSError, IOError): 172 | print('Error: the attachment does not match the mimetype') 173 | err = True 174 | else: 175 | fpo.close() 176 | if err: 177 | # Return EX_DATAERR code available 178 | # at sysexits.h file 179 | # (see http://www.postfix.org/pipe.8.html) 180 | sys.exit(65) 181 | 182 | 183 | def import_report_from_stdin(): 184 | """Parse a report from stdin.""" 185 | content = six.StringIO() 186 | for line in fileinput.input([]): 187 | content.write(line) 188 | content.seek(0) 189 | 190 | if not content: 191 | return 192 | import_report_from_email(content) 193 | 194 | 195 | def import_from_imap(options): 196 | """Import reports from an IMAP mailbox.""" 197 | obj = imaplib.IMAP4_SSL if options["ssl"] else imaplib.IMAP4 198 | conn = obj(options["host"]) 199 | username = input("Username: ") 200 | password = getpass.getpass(prompt="Password: ") 201 | conn.login(username, password) 202 | conn.select(options["mailbox"]) 203 | type, msg_ids = conn.search(None, "ALL") 204 | for msg_id in msg_ids[0].split(): 205 | typ, content = conn.fetch(msg_id, "(RFC822)") 206 | for response_part in content: 207 | if isinstance(response_part, tuple): 208 | import_report_from_email(response_part[1]) 209 | conn.close() 210 | -------------------------------------------------------------------------------- /modoboa_dmarc/locale/br/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # 5 | # Translators: 6 | # Irriep Nala Novram , 2019 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: Modoboa\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2020-05-06 14:05+0200\n" 12 | "PO-Revision-Date: 2020-05-06 12:07+0000\n" 13 | "Last-Translator: Antoine Nguyen \n" 14 | "Language-Team: Breton (http://www.transifex.com/tonio/modoboa/language/br/)\n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=UTF-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | "Language: br\n" 19 | "Plural-Forms: nplurals=5; plural=((n%10 == 1) && (n%100 != 11) && (n%100 !=71) && (n%100 !=91) ? 0 :(n%10 == 2) && (n%100 != 12) && (n%100 !=72) && (n%100 !=92) ? 1 :(n%10 ==3 || n%10==4 || n%10==9) && (n%100 < 10 || n% 100 > 19) && (n%100 < 70 || n%100 > 79) && (n%100 < 90 || n%100 > 99) ? 2 :(n != 0 && n % 1000000 == 0) ? 3 : 4);\n" 20 | 21 | #: forms.py:35 22 | msgid "DNS settings" 23 | msgstr "Arventennoù DNS" 24 | 25 | #: forms.py:38 26 | msgid "Enable reverse lookups" 27 | msgstr "Enaouiñ an enklaskoù eilpennet" 28 | 29 | #: forms.py:41 30 | msgid "Enable reverse DNS lookups (reports will be longer to display)" 31 | msgstr "Enaouiñ an enklaskoù DNS eilpennet (danevelloù a vo hiroc'h da vezañ diskouezet)" 32 | 33 | #: handlers.py:20 34 | msgid "Show DMARC report for {}" 35 | msgstr "Diskouez an danevell DMARC evit {}" 36 | 37 | #: models.py:10 models.py:16 38 | msgid "None" 39 | msgstr "Hini ebet" 40 | 41 | #: models.py:11 42 | msgid "Quarantine" 43 | msgstr "Dispell" 44 | 45 | #: models.py:12 46 | msgid "Reject" 47 | msgstr "Nac'hañ" 48 | 49 | #: models.py:17 50 | msgid "Neutral" 51 | msgstr "Neptu" 52 | 53 | #: models.py:18 54 | msgid "Pass" 55 | msgstr "Tremen" 56 | 57 | #: models.py:19 58 | msgid "Fail" 59 | msgstr "Sac'hadenn" 60 | 61 | #: models.py:20 62 | msgid "Temporary error" 63 | msgstr "Fazi padennek" 64 | 65 | #: models.py:21 66 | msgid "Permanent error" 67 | msgstr "Fazi peurbadus" 68 | 69 | #: models.py:25 70 | msgid "Policy" 71 | msgstr "Politikerezh" 72 | 73 | #: models.py:29 74 | msgid "Soft failure" 75 | msgstr "Sac'hadenn wan" 76 | 77 | #: modo_extension.py:16 78 | msgid "DMARC tools" 79 | msgstr "Ostilhoù DMARC" 80 | 81 | #: modo_extension.py:19 82 | msgid "A set of tools to ease DMARC integration" 83 | msgstr "Un dastumad ostilhoù da aesaat enframmañ DMARC" 84 | 85 | #: templates/modoboa_dmarc/_result_detail.html:6 86 | msgid "Total" 87 | msgstr "Hollad" 88 | 89 | #: templates/modoboa_dmarc/domain_report.html:21 90 | #: templates/modoboa_dmarc/domain_report.html:26 91 | msgid "Fully aligned" 92 | msgstr "Linennet penn-da-benn" 93 | 94 | #: templates/modoboa_dmarc/domain_report.html:22 95 | #: templates/modoboa_dmarc/domain_report.html:27 96 | msgid "Partially aligned" 97 | msgstr "Linennet dre an hanter" 98 | 99 | #: templates/modoboa_dmarc/domain_report.html:23 100 | #: templates/modoboa_dmarc/domain_report.html:28 101 | msgid "Failed" 102 | msgstr "C'hwitet" 103 | 104 | #: templates/modoboa_dmarc/domain_report.html:50 105 | msgid "DMARC report for" 106 | msgstr "Danevell DMARC evit" 107 | 108 | #: templates/modoboa_dmarc/domain_report.html:74 109 | msgid "processed" 110 | msgstr "tretet" 111 | 112 | #: templates/modoboa_dmarc/domain_report.html:83 113 | msgid "fully aligned" 114 | msgstr "linennet penn-da-benn" 115 | 116 | #: templates/modoboa_dmarc/domain_report.html:92 117 | msgid "partially aligned" 118 | msgstr "linennet dre an hanter" 119 | 120 | #: templates/modoboa_dmarc/domain_report.html:101 121 | msgid "failed" 122 | msgstr "c'hwitet" 123 | 124 | #: templates/modoboa_dmarc/domain_report.html:111 125 | msgid "Display details" 126 | msgstr "Diskouez ar munudoù" 127 | 128 | #: templates/modoboa_dmarc/domain_report.html:115 129 | msgid "Trusted sources" 130 | msgstr "Mammennoù fizius" 131 | 132 | #: templates/modoboa_dmarc/domain_report.html:123 133 | msgid "Unknown sources / Threats" 134 | msgstr "Mammennoù dizanv/Gourdrouzioù" 135 | 136 | #: templatetags/dmarc_tags.py:59 views.py:120 137 | msgid "Not resolved" 138 | msgstr "" 139 | -------------------------------------------------------------------------------- /modoboa_dmarc/locale/cs_CZ/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # 5 | # Translators: 6 | # Jan Laufik, 2016 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: Modoboa\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2020-05-06 14:05+0200\n" 12 | "PO-Revision-Date: 2017-03-03 08:18+0000\n" 13 | "Last-Translator: Antoine Nguyen \n" 14 | "Language-Team: Czech (Czech Republic) (http://www.transifex.com/tonio/" 15 | "modoboa/language/cs_CZ/)\n" 16 | "Language: cs_CZ\n" 17 | "MIME-Version: 1.0\n" 18 | "Content-Type: text/plain; charset=UTF-8\n" 19 | "Content-Transfer-Encoding: 8bit\n" 20 | "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" 21 | 22 | #: forms.py:35 23 | msgid "DNS settings" 24 | msgstr "" 25 | 26 | #: forms.py:38 27 | msgid "Enable reverse lookups" 28 | msgstr "" 29 | 30 | #: forms.py:41 31 | msgid "Enable reverse DNS lookups (reports will be longer to display)" 32 | msgstr "" 33 | 34 | #: handlers.py:20 35 | msgid "Show DMARC report for {}" 36 | msgstr "Zobrazit DMARC zprávu pro {}" 37 | 38 | #: models.py:10 models.py:16 39 | msgid "None" 40 | msgstr "Nic" 41 | 42 | #: models.py:11 43 | msgid "Quarantine" 44 | msgstr "Karanténa" 45 | 46 | #: models.py:12 47 | msgid "Reject" 48 | msgstr "Odmítnout" 49 | 50 | #: models.py:17 51 | msgid "Neutral" 52 | msgstr "Neutrální" 53 | 54 | #: models.py:18 55 | msgid "Pass" 56 | msgstr "Úspěch" 57 | 58 | #: models.py:19 59 | msgid "Fail" 60 | msgstr "Selhání" 61 | 62 | #: models.py:20 63 | msgid "Temporary error" 64 | msgstr "Dočasná chyba" 65 | 66 | #: models.py:21 67 | msgid "Permanent error" 68 | msgstr "Permanentní chyba" 69 | 70 | #: models.py:25 71 | msgid "Policy" 72 | msgstr "Politika" 73 | 74 | #: models.py:29 75 | msgid "Soft failure" 76 | msgstr "Selhání bez odmítnutí" 77 | 78 | #: modo_extension.py:16 79 | msgid "DMARC tools" 80 | msgstr "DMARC nástroje" 81 | 82 | #: modo_extension.py:19 83 | msgid "A set of tools to ease DMARC integration" 84 | msgstr "Sada nástrojů pro zjednodušení integrace DMARC" 85 | 86 | #: templates/modoboa_dmarc/_result_detail.html:6 87 | msgid "Total" 88 | msgstr "Celkem" 89 | 90 | #: templates/modoboa_dmarc/domain_report.html:21 91 | #: templates/modoboa_dmarc/domain_report.html:26 92 | msgid "Fully aligned" 93 | msgstr "Plně v souladu" 94 | 95 | #: templates/modoboa_dmarc/domain_report.html:22 96 | #: templates/modoboa_dmarc/domain_report.html:27 97 | msgid "Partially aligned" 98 | msgstr "Částečně v souladu" 99 | 100 | #: templates/modoboa_dmarc/domain_report.html:23 101 | #: templates/modoboa_dmarc/domain_report.html:28 102 | msgid "Failed" 103 | msgstr "Selhalo" 104 | 105 | #: templates/modoboa_dmarc/domain_report.html:50 106 | msgid "DMARC report for" 107 | msgstr "DMARC zpráva pro" 108 | 109 | #: templates/modoboa_dmarc/domain_report.html:74 110 | msgid "processed" 111 | msgstr "zpracované" 112 | 113 | #: templates/modoboa_dmarc/domain_report.html:83 114 | msgid "fully aligned" 115 | msgstr "plně v souladu" 116 | 117 | #: templates/modoboa_dmarc/domain_report.html:92 118 | msgid "partially aligned" 119 | msgstr "částečně v souladu" 120 | 121 | #: templates/modoboa_dmarc/domain_report.html:101 122 | msgid "failed" 123 | msgstr "selhalo" 124 | 125 | #: templates/modoboa_dmarc/domain_report.html:111 126 | msgid "Display details" 127 | msgstr "Zobrazit podrobnosti" 128 | 129 | #: templates/modoboa_dmarc/domain_report.html:115 130 | msgid "Trusted sources" 131 | msgstr "Důvěryhodné zdroje" 132 | 133 | #: templates/modoboa_dmarc/domain_report.html:123 134 | msgid "Unknown sources / Threats" 135 | msgstr "Neznáme zdroje / Hrozby" 136 | 137 | #: templatetags/dmarc_tags.py:59 views.py:120 138 | msgid "Not resolved" 139 | msgstr "" 140 | -------------------------------------------------------------------------------- /modoboa_dmarc/locale/de/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # 5 | # Translators: 6 | # controlc.de, 2015 7 | # Tobias Strobel, 2016 8 | msgid "" 9 | msgstr "" 10 | "Project-Id-Version: Modoboa\n" 11 | "Report-Msgid-Bugs-To: \n" 12 | "POT-Creation-Date: 2020-05-06 14:05+0200\n" 13 | "PO-Revision-Date: 2017-03-03 08:18+0000\n" 14 | "Last-Translator: Antoine Nguyen \n" 15 | "Language-Team: German (http://www.transifex.com/tonio/modoboa/language/de/)\n" 16 | "Language: de\n" 17 | "MIME-Version: 1.0\n" 18 | "Content-Type: text/plain; charset=UTF-8\n" 19 | "Content-Transfer-Encoding: 8bit\n" 20 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 21 | 22 | #: forms.py:35 23 | msgid "DNS settings" 24 | msgstr "" 25 | 26 | #: forms.py:38 27 | msgid "Enable reverse lookups" 28 | msgstr "" 29 | 30 | #: forms.py:41 31 | msgid "Enable reverse DNS lookups (reports will be longer to display)" 32 | msgstr "" 33 | 34 | #: handlers.py:20 35 | msgid "Show DMARC report for {}" 36 | msgstr "DMARC-Bericht für {} anzeigen" 37 | 38 | #: models.py:10 models.py:16 39 | msgid "None" 40 | msgstr "Nichts" 41 | 42 | #: models.py:11 43 | msgid "Quarantine" 44 | msgstr "Quarantäne" 45 | 46 | #: models.py:12 47 | msgid "Reject" 48 | msgstr "Ablehnung" 49 | 50 | #: models.py:17 51 | msgid "Neutral" 52 | msgstr "Neutral" 53 | 54 | #: models.py:18 55 | msgid "Pass" 56 | msgstr "Genehmigen" 57 | 58 | #: models.py:19 59 | msgid "Fail" 60 | msgstr "Abbruch" 61 | 62 | #: models.py:20 63 | msgid "Temporary error" 64 | msgstr "Temporärer Fehler" 65 | 66 | #: models.py:21 67 | msgid "Permanent error" 68 | msgstr "Permanenter Fehler" 69 | 70 | #: models.py:25 71 | msgid "Policy" 72 | msgstr "Richtlinie" 73 | 74 | #: models.py:29 75 | msgid "Soft failure" 76 | msgstr "Schwacher Fehler" 77 | 78 | #: modo_extension.py:16 79 | msgid "DMARC tools" 80 | msgstr "DMARC-Tools" 81 | 82 | #: modo_extension.py:19 83 | msgid "A set of tools to ease DMARC integration" 84 | msgstr "Ein Set von Tools um die DMARC-Integration zu vereinfachen" 85 | 86 | #: templates/modoboa_dmarc/_result_detail.html:6 87 | msgid "Total" 88 | msgstr "Total" 89 | 90 | #: templates/modoboa_dmarc/domain_report.html:21 91 | #: templates/modoboa_dmarc/domain_report.html:26 92 | msgid "Fully aligned" 93 | msgstr "Voll zugeordnet" 94 | 95 | #: templates/modoboa_dmarc/domain_report.html:22 96 | #: templates/modoboa_dmarc/domain_report.html:27 97 | msgid "Partially aligned" 98 | msgstr "Teilweise zugeordnet" 99 | 100 | #: templates/modoboa_dmarc/domain_report.html:23 101 | #: templates/modoboa_dmarc/domain_report.html:28 102 | msgid "Failed" 103 | msgstr "Fehlgeschlagen" 104 | 105 | #: templates/modoboa_dmarc/domain_report.html:50 106 | msgid "DMARC report for" 107 | msgstr "DMARC-Bericht für" 108 | 109 | #: templates/modoboa_dmarc/domain_report.html:74 110 | msgid "processed" 111 | msgstr "durchgeführt" 112 | 113 | #: templates/modoboa_dmarc/domain_report.html:83 114 | msgid "fully aligned" 115 | msgstr "voll zugeordnet" 116 | 117 | #: templates/modoboa_dmarc/domain_report.html:92 118 | msgid "partially aligned" 119 | msgstr "teilweise zugeordnet" 120 | 121 | #: templates/modoboa_dmarc/domain_report.html:101 122 | msgid "failed" 123 | msgstr "fehlgeschlagen" 124 | 125 | #: templates/modoboa_dmarc/domain_report.html:111 126 | msgid "Display details" 127 | msgstr "Anzeigedetails" 128 | 129 | #: templates/modoboa_dmarc/domain_report.html:115 130 | msgid "Trusted sources" 131 | msgstr "Vertraute Quellen" 132 | 133 | #: templates/modoboa_dmarc/domain_report.html:123 134 | msgid "Unknown sources / Threats" 135 | msgstr "Unbekannte Quellen / Bedrohungen" 136 | 137 | #: templatetags/dmarc_tags.py:59 views.py:120 138 | msgid "Not resolved" 139 | msgstr "" 140 | -------------------------------------------------------------------------------- /modoboa_dmarc/locale/el_GR/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # 5 | # Translators: 6 | # Giannis Kapetanakis , 2016 7 | # Kostas Moumouris , 2017 8 | msgid "" 9 | msgstr "" 10 | "Project-Id-Version: Modoboa\n" 11 | "Report-Msgid-Bugs-To: \n" 12 | "POT-Creation-Date: 2020-05-06 14:05+0200\n" 13 | "PO-Revision-Date: 2017-10-12 09:55+0000\n" 14 | "Last-Translator: Kostas Moumouris \n" 15 | "Language-Team: Greek (Greece) (http://www.transifex.com/tonio/modoboa/" 16 | "language/el_GR/)\n" 17 | "Language: el_GR\n" 18 | "MIME-Version: 1.0\n" 19 | "Content-Type: text/plain; charset=UTF-8\n" 20 | "Content-Transfer-Encoding: 8bit\n" 21 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 22 | 23 | #: forms.py:35 24 | msgid "DNS settings" 25 | msgstr "Ρυθμίσεις DNS" 26 | 27 | #: forms.py:38 28 | msgid "Enable reverse lookups" 29 | msgstr "Ενεργοποίηση ανάστροφης αναζήτησης reverse lookups" 30 | 31 | #: forms.py:41 32 | msgid "Enable reverse DNS lookups (reports will be longer to display)" 33 | msgstr "Ενεργοποίηση reverse DNS lookups (η αναφορές θα καθυστερούν)" 34 | 35 | #: handlers.py:20 36 | msgid "Show DMARC report for {}" 37 | msgstr "Εμφάνιση της έκθεσης DMARC του {}" 38 | 39 | #: models.py:10 models.py:16 40 | msgid "None" 41 | msgstr "Κανένα" 42 | 43 | #: models.py:11 44 | msgid "Quarantine" 45 | msgstr "Καραντίνα" 46 | 47 | #: models.py:12 48 | msgid "Reject" 49 | msgstr "Απόρριψη" 50 | 51 | #: models.py:17 52 | msgid "Neutral" 53 | msgstr "Ουδέτερο" 54 | 55 | #: models.py:18 56 | msgid "Pass" 57 | msgstr "Εγκρίθηκε" 58 | 59 | #: models.py:19 60 | msgid "Fail" 61 | msgstr "Απορρίφθηκε" 62 | 63 | #: models.py:20 64 | msgid "Temporary error" 65 | msgstr "προσωρινό σφάλμα" 66 | 67 | #: models.py:21 68 | msgid "Permanent error" 69 | msgstr "Μόνιμο σφάλμα" 70 | 71 | #: models.py:25 72 | msgid "Policy" 73 | msgstr "Πολιτική" 74 | 75 | #: models.py:29 76 | msgid "Soft failure" 77 | msgstr "Πρόβλημα Soft" 78 | 79 | #: modo_extension.py:16 80 | msgid "DMARC tools" 81 | msgstr "Εργαλεία DMARC" 82 | 83 | #: modo_extension.py:19 84 | msgid "A set of tools to ease DMARC integration" 85 | msgstr "Ένα σετ εργαλείων για την απλοποίηση της ενσωμάτωσης του DMARC" 86 | 87 | #: templates/modoboa_dmarc/_result_detail.html:6 88 | msgid "Total" 89 | msgstr "Σύνολο" 90 | 91 | #: templates/modoboa_dmarc/domain_report.html:21 92 | #: templates/modoboa_dmarc/domain_report.html:26 93 | msgid "Fully aligned" 94 | msgstr "Πλήρως ευθυγραμμισμένο" 95 | 96 | #: templates/modoboa_dmarc/domain_report.html:22 97 | #: templates/modoboa_dmarc/domain_report.html:27 98 | msgid "Partially aligned" 99 | msgstr "Μερικός ευθυγραμισμένα" 100 | 101 | #: templates/modoboa_dmarc/domain_report.html:23 102 | #: templates/modoboa_dmarc/domain_report.html:28 103 | msgid "Failed" 104 | msgstr "Απέτυχε" 105 | 106 | #: templates/modoboa_dmarc/domain_report.html:50 107 | msgid "DMARC report for" 108 | msgstr "Έκθεση DMARC του" 109 | 110 | #: templates/modoboa_dmarc/domain_report.html:74 111 | msgid "processed" 112 | msgstr "Επεξεργάστηκε" 113 | 114 | #: templates/modoboa_dmarc/domain_report.html:83 115 | msgid "fully aligned" 116 | msgstr "πλήρως ευθυγραμμισμένο" 117 | 118 | #: templates/modoboa_dmarc/domain_report.html:92 119 | msgid "partially aligned" 120 | msgstr "Μερικώς ευθυγραμμισμένο" 121 | 122 | #: templates/modoboa_dmarc/domain_report.html:101 123 | msgid "failed" 124 | msgstr "απέτυχε" 125 | 126 | #: templates/modoboa_dmarc/domain_report.html:111 127 | msgid "Display details" 128 | msgstr "Προβολή λεπτομερειών" 129 | 130 | #: templates/modoboa_dmarc/domain_report.html:115 131 | msgid "Trusted sources" 132 | msgstr "Αξιόπιστες πηγές" 133 | 134 | #: templates/modoboa_dmarc/domain_report.html:123 135 | msgid "Unknown sources / Threats" 136 | msgstr "Άγνωστες πηγές / Απειλές" 137 | 138 | #: templatetags/dmarc_tags.py:59 views.py:120 139 | msgid "Not resolved" 140 | msgstr "" 141 | -------------------------------------------------------------------------------- /modoboa_dmarc/locale/en/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: PACKAGE VERSION\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2020-05-06 14:05+0200\n" 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 13 | "Last-Translator: FULL NAME \n" 14 | "Language-Team: LANGUAGE \n" 15 | "Language: \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | 20 | #: forms.py:35 21 | msgid "DNS settings" 22 | msgstr "" 23 | 24 | #: forms.py:38 25 | msgid "Enable reverse lookups" 26 | msgstr "" 27 | 28 | #: forms.py:41 29 | msgid "Enable reverse DNS lookups (reports will be longer to display)" 30 | msgstr "" 31 | 32 | #: handlers.py:20 33 | msgid "Show DMARC report for {}" 34 | msgstr "" 35 | 36 | #: models.py:10 models.py:16 37 | msgid "None" 38 | msgstr "" 39 | 40 | #: models.py:11 41 | msgid "Quarantine" 42 | msgstr "" 43 | 44 | #: models.py:12 45 | msgid "Reject" 46 | msgstr "" 47 | 48 | #: models.py:17 49 | msgid "Neutral" 50 | msgstr "" 51 | 52 | #: models.py:18 53 | msgid "Pass" 54 | msgstr "" 55 | 56 | #: models.py:19 57 | msgid "Fail" 58 | msgstr "" 59 | 60 | #: models.py:20 61 | msgid "Temporary error" 62 | msgstr "" 63 | 64 | #: models.py:21 65 | msgid "Permanent error" 66 | msgstr "" 67 | 68 | #: models.py:25 69 | msgid "Policy" 70 | msgstr "" 71 | 72 | #: models.py:29 73 | msgid "Soft failure" 74 | msgstr "" 75 | 76 | #: modo_extension.py:16 77 | msgid "DMARC tools" 78 | msgstr "" 79 | 80 | #: modo_extension.py:19 81 | msgid "A set of tools to ease DMARC integration" 82 | msgstr "" 83 | 84 | #: templates/modoboa_dmarc/_result_detail.html:6 85 | msgid "Total" 86 | msgstr "" 87 | 88 | #: templates/modoboa_dmarc/domain_report.html:21 89 | #: templates/modoboa_dmarc/domain_report.html:26 90 | msgid "Fully aligned" 91 | msgstr "" 92 | 93 | #: templates/modoboa_dmarc/domain_report.html:22 94 | #: templates/modoboa_dmarc/domain_report.html:27 95 | msgid "Partially aligned" 96 | msgstr "" 97 | 98 | #: templates/modoboa_dmarc/domain_report.html:23 99 | #: templates/modoboa_dmarc/domain_report.html:28 100 | msgid "Failed" 101 | msgstr "" 102 | 103 | #: templates/modoboa_dmarc/domain_report.html:50 104 | msgid "DMARC report for" 105 | msgstr "" 106 | 107 | #: templates/modoboa_dmarc/domain_report.html:74 108 | msgid "processed" 109 | msgstr "" 110 | 111 | #: templates/modoboa_dmarc/domain_report.html:83 112 | msgid "fully aligned" 113 | msgstr "" 114 | 115 | #: templates/modoboa_dmarc/domain_report.html:92 116 | msgid "partially aligned" 117 | msgstr "" 118 | 119 | #: templates/modoboa_dmarc/domain_report.html:101 120 | msgid "failed" 121 | msgstr "" 122 | 123 | #: templates/modoboa_dmarc/domain_report.html:111 124 | msgid "Display details" 125 | msgstr "" 126 | 127 | #: templates/modoboa_dmarc/domain_report.html:115 128 | msgid "Trusted sources" 129 | msgstr "" 130 | 131 | #: templates/modoboa_dmarc/domain_report.html:123 132 | msgid "Unknown sources / Threats" 133 | msgstr "" 134 | 135 | #: templatetags/dmarc_tags.py:59 views.py:120 136 | msgid "Not resolved" 137 | msgstr "" 138 | -------------------------------------------------------------------------------- /modoboa_dmarc/locale/fr/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # 5 | # Translators: 6 | # Antoine Nguyen , 2017,2020 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: Modoboa\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2020-05-06 14:05+0200\n" 12 | "PO-Revision-Date: 2020-05-06 12:07+0000\n" 13 | "Last-Translator: Antoine Nguyen \n" 14 | "Language-Team: French (http://www.transifex.com/tonio/modoboa/language/fr/)\n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=UTF-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | "Language: fr\n" 19 | "Plural-Forms: nplurals=2; plural=(n > 1);\n" 20 | 21 | #: forms.py:35 22 | msgid "DNS settings" 23 | msgstr "Paramètres DNS" 24 | 25 | #: forms.py:38 26 | msgid "Enable reverse lookups" 27 | msgstr "Activer les recherches inversées" 28 | 29 | #: forms.py:41 30 | msgid "Enable reverse DNS lookups (reports will be longer to display)" 31 | msgstr "Activer les recherches DNS inversées (les rapports seront plus longs à afficher)" 32 | 33 | #: handlers.py:20 34 | msgid "Show DMARC report for {}" 35 | msgstr "Afficher le rapport DMARC pour {}" 36 | 37 | #: models.py:10 models.py:16 38 | msgid "None" 39 | msgstr "Aucun" 40 | 41 | #: models.py:11 42 | msgid "Quarantine" 43 | msgstr "Quarantaine" 44 | 45 | #: models.py:12 46 | msgid "Reject" 47 | msgstr "Rejeter" 48 | 49 | #: models.py:17 50 | msgid "Neutral" 51 | msgstr "Neutre" 52 | 53 | #: models.py:18 54 | msgid "Pass" 55 | msgstr "Succès" 56 | 57 | #: models.py:19 58 | msgid "Fail" 59 | msgstr "Echec" 60 | 61 | #: models.py:20 62 | msgid "Temporary error" 63 | msgstr "Erreur temporaire" 64 | 65 | #: models.py:21 66 | msgid "Permanent error" 67 | msgstr "Erreur permanente" 68 | 69 | #: models.py:25 70 | msgid "Policy" 71 | msgstr "Politique" 72 | 73 | #: models.py:29 74 | msgid "Soft failure" 75 | msgstr "Erreur soft" 76 | 77 | #: modo_extension.py:16 78 | msgid "DMARC tools" 79 | msgstr "Outils DMARC" 80 | 81 | #: modo_extension.py:19 82 | msgid "A set of tools to ease DMARC integration" 83 | msgstr "Un jeu d'outils pour faciliter l'intégration de DMARC" 84 | 85 | #: templates/modoboa_dmarc/_result_detail.html:6 86 | msgid "Total" 87 | msgstr "Total" 88 | 89 | #: templates/modoboa_dmarc/domain_report.html:21 90 | #: templates/modoboa_dmarc/domain_report.html:26 91 | msgid "Fully aligned" 92 | msgstr "Alignement complet" 93 | 94 | #: templates/modoboa_dmarc/domain_report.html:22 95 | #: templates/modoboa_dmarc/domain_report.html:27 96 | msgid "Partially aligned" 97 | msgstr "Alignement partiel" 98 | 99 | #: templates/modoboa_dmarc/domain_report.html:23 100 | #: templates/modoboa_dmarc/domain_report.html:28 101 | msgid "Failed" 102 | msgstr "En échec" 103 | 104 | #: templates/modoboa_dmarc/domain_report.html:50 105 | msgid "DMARC report for" 106 | msgstr "Rapport DMARC pour" 107 | 108 | #: templates/modoboa_dmarc/domain_report.html:74 109 | msgid "processed" 110 | msgstr "traité(s)" 111 | 112 | #: templates/modoboa_dmarc/domain_report.html:83 113 | msgid "fully aligned" 114 | msgstr "complètement aligné(s)" 115 | 116 | #: templates/modoboa_dmarc/domain_report.html:92 117 | msgid "partially aligned" 118 | msgstr "partiellement aligné(s)" 119 | 120 | #: templates/modoboa_dmarc/domain_report.html:101 121 | msgid "failed" 122 | msgstr "en échec" 123 | 124 | #: templates/modoboa_dmarc/domain_report.html:111 125 | msgid "Display details" 126 | msgstr "Afficher les détails" 127 | 128 | #: templates/modoboa_dmarc/domain_report.html:115 129 | msgid "Trusted sources" 130 | msgstr "Sources de confiance" 131 | 132 | #: templates/modoboa_dmarc/domain_report.html:123 133 | msgid "Unknown sources / Threats" 134 | msgstr "Sources inconnues / Menaces" 135 | 136 | #: templatetags/dmarc_tags.py:59 views.py:120 137 | msgid "Not resolved" 138 | msgstr "Non résolu" 139 | -------------------------------------------------------------------------------- /modoboa_dmarc/locale/it/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # 5 | # Translators: 6 | # Rocco , 2015 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: Modoboa\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2020-05-06 14:05+0200\n" 12 | "PO-Revision-Date: 2017-03-03 08:18+0000\n" 13 | "Last-Translator: Antoine Nguyen \n" 14 | "Language-Team: Italian (http://www.transifex.com/tonio/modoboa/language/" 15 | "it/)\n" 16 | "Language: it\n" 17 | "MIME-Version: 1.0\n" 18 | "Content-Type: text/plain; charset=UTF-8\n" 19 | "Content-Transfer-Encoding: 8bit\n" 20 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 21 | 22 | #: forms.py:35 23 | msgid "DNS settings" 24 | msgstr "" 25 | 26 | #: forms.py:38 27 | msgid "Enable reverse lookups" 28 | msgstr "" 29 | 30 | #: forms.py:41 31 | msgid "Enable reverse DNS lookups (reports will be longer to display)" 32 | msgstr "" 33 | 34 | #: handlers.py:20 35 | msgid "Show DMARC report for {}" 36 | msgstr "Mostra DMARC report per {}" 37 | 38 | #: models.py:10 models.py:16 39 | msgid "None" 40 | msgstr "Nessuno" 41 | 42 | #: models.py:11 43 | msgid "Quarantine" 44 | msgstr "Quarantina" 45 | 46 | #: models.py:12 47 | msgid "Reject" 48 | msgstr "Rifiuta" 49 | 50 | #: models.py:17 51 | msgid "Neutral" 52 | msgstr "Neutrale" 53 | 54 | #: models.py:18 55 | msgid "Pass" 56 | msgstr "Passa" 57 | 58 | #: models.py:19 59 | msgid "Fail" 60 | msgstr "Fallito" 61 | 62 | #: models.py:20 63 | msgid "Temporary error" 64 | msgstr "Errore temporaneo" 65 | 66 | #: models.py:21 67 | msgid "Permanent error" 68 | msgstr "Errore permanente" 69 | 70 | #: models.py:25 71 | msgid "Policy" 72 | msgstr "Policy" 73 | 74 | #: models.py:29 75 | msgid "Soft failure" 76 | msgstr "Piccolo problema" 77 | 78 | #: modo_extension.py:16 79 | msgid "DMARC tools" 80 | msgstr "Strumenti DMARC" 81 | 82 | #: modo_extension.py:19 83 | msgid "A set of tools to ease DMARC integration" 84 | msgstr "Una serie di strumenti per facilitare l'integrazione DMARC" 85 | 86 | #: templates/modoboa_dmarc/_result_detail.html:6 87 | msgid "Total" 88 | msgstr "Totale" 89 | 90 | #: templates/modoboa_dmarc/domain_report.html:21 91 | #: templates/modoboa_dmarc/domain_report.html:26 92 | msgid "Fully aligned" 93 | msgstr "Completamente allineato" 94 | 95 | #: templates/modoboa_dmarc/domain_report.html:22 96 | #: templates/modoboa_dmarc/domain_report.html:27 97 | msgid "Partially aligned" 98 | msgstr "Parzialmente allineato" 99 | 100 | #: templates/modoboa_dmarc/domain_report.html:23 101 | #: templates/modoboa_dmarc/domain_report.html:28 102 | msgid "Failed" 103 | msgstr "Fallito" 104 | 105 | #: templates/modoboa_dmarc/domain_report.html:50 106 | msgid "DMARC report for" 107 | msgstr "Rapporto DMARC per" 108 | 109 | #: templates/modoboa_dmarc/domain_report.html:74 110 | msgid "processed" 111 | msgstr "Processati" 112 | 113 | #: templates/modoboa_dmarc/domain_report.html:83 114 | msgid "fully aligned" 115 | msgstr "Completamente allineato" 116 | 117 | #: templates/modoboa_dmarc/domain_report.html:92 118 | msgid "partially aligned" 119 | msgstr "Allineato parzialmente" 120 | 121 | #: templates/modoboa_dmarc/domain_report.html:101 122 | msgid "failed" 123 | msgstr "Fallito" 124 | 125 | #: templates/modoboa_dmarc/domain_report.html:111 126 | msgid "Display details" 127 | msgstr "Mostra dettagli" 128 | 129 | #: templates/modoboa_dmarc/domain_report.html:115 130 | msgid "Trusted sources" 131 | msgstr "Fonti attendibili" 132 | 133 | #: templates/modoboa_dmarc/domain_report.html:123 134 | msgid "Unknown sources / Threats" 135 | msgstr "Fonti sconosciute / Minaccie" 136 | 137 | #: templatetags/dmarc_tags.py:59 views.py:120 138 | msgid "Not resolved" 139 | msgstr "" 140 | -------------------------------------------------------------------------------- /modoboa_dmarc/locale/ja_JP/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # 5 | # Translators: 6 | # cwatanab , 2016 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: Modoboa\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2020-05-06 14:05+0200\n" 12 | "PO-Revision-Date: 2017-03-03 08:18+0000\n" 13 | "Last-Translator: Antoine Nguyen \n" 14 | "Language-Team: Japanese (Japan) (http://www.transifex.com/tonio/modoboa/" 15 | "language/ja_JP/)\n" 16 | "Language: ja_JP\n" 17 | "MIME-Version: 1.0\n" 18 | "Content-Type: text/plain; charset=UTF-8\n" 19 | "Content-Transfer-Encoding: 8bit\n" 20 | "Plural-Forms: nplurals=1; plural=0;\n" 21 | 22 | #: forms.py:35 23 | msgid "DNS settings" 24 | msgstr "" 25 | 26 | #: forms.py:38 27 | msgid "Enable reverse lookups" 28 | msgstr "" 29 | 30 | #: forms.py:41 31 | msgid "Enable reverse DNS lookups (reports will be longer to display)" 32 | msgstr "" 33 | 34 | #: handlers.py:20 35 | msgid "Show DMARC report for {}" 36 | msgstr "{} の DMARC レポートを表示" 37 | 38 | #: models.py:10 models.py:16 39 | msgid "None" 40 | msgstr "None" 41 | 42 | #: models.py:11 43 | msgid "Quarantine" 44 | msgstr "Quarantine" 45 | 46 | #: models.py:12 47 | msgid "Reject" 48 | msgstr "Reject" 49 | 50 | #: models.py:17 51 | msgid "Neutral" 52 | msgstr "Neutral" 53 | 54 | #: models.py:18 55 | msgid "Pass" 56 | msgstr "Pass" 57 | 58 | #: models.py:19 59 | msgid "Fail" 60 | msgstr "Fail" 61 | 62 | #: models.py:20 63 | msgid "Temporary error" 64 | msgstr "Temporary error" 65 | 66 | #: models.py:21 67 | msgid "Permanent error" 68 | msgstr "Permanent error" 69 | 70 | #: models.py:25 71 | msgid "Policy" 72 | msgstr "Policy" 73 | 74 | #: models.py:29 75 | msgid "Soft failure" 76 | msgstr "Soft failure" 77 | 78 | #: modo_extension.py:16 79 | msgid "DMARC tools" 80 | msgstr "DMARC ツール" 81 | 82 | #: modo_extension.py:19 83 | msgid "A set of tools to ease DMARC integration" 84 | msgstr "DMARC 統合のためのツールセット" 85 | 86 | #: templates/modoboa_dmarc/_result_detail.html:6 87 | msgid "Total" 88 | msgstr "Total" 89 | 90 | #: templates/modoboa_dmarc/domain_report.html:21 91 | #: templates/modoboa_dmarc/domain_report.html:26 92 | msgid "Fully aligned" 93 | msgstr "Fully aligned" 94 | 95 | #: templates/modoboa_dmarc/domain_report.html:22 96 | #: templates/modoboa_dmarc/domain_report.html:27 97 | msgid "Partially aligned" 98 | msgstr "Partially aligned" 99 | 100 | #: templates/modoboa_dmarc/domain_report.html:23 101 | #: templates/modoboa_dmarc/domain_report.html:28 102 | msgid "Failed" 103 | msgstr "Failed" 104 | 105 | #: templates/modoboa_dmarc/domain_report.html:50 106 | msgid "DMARC report for" 107 | msgstr "DMARC レポート" 108 | 109 | #: templates/modoboa_dmarc/domain_report.html:74 110 | msgid "processed" 111 | msgstr "processed" 112 | 113 | #: templates/modoboa_dmarc/domain_report.html:83 114 | msgid "fully aligned" 115 | msgstr "fully aligned" 116 | 117 | #: templates/modoboa_dmarc/domain_report.html:92 118 | msgid "partially aligned" 119 | msgstr "partially aligned" 120 | 121 | #: templates/modoboa_dmarc/domain_report.html:101 122 | msgid "failed" 123 | msgstr "failed" 124 | 125 | #: templates/modoboa_dmarc/domain_report.html:111 126 | msgid "Display details" 127 | msgstr "詳細を表示" 128 | 129 | #: templates/modoboa_dmarc/domain_report.html:115 130 | msgid "Trusted sources" 131 | msgstr "Trusted sources" 132 | 133 | #: templates/modoboa_dmarc/domain_report.html:123 134 | msgid "Unknown sources / Threats" 135 | msgstr "Unknown sources / Threats" 136 | 137 | #: templatetags/dmarc_tags.py:59 views.py:120 138 | msgid "Not resolved" 139 | msgstr "" 140 | -------------------------------------------------------------------------------- /modoboa_dmarc/locale/nl_NL/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # 5 | # Translators: 6 | # Harrie v.d. Vegt , 2016 7 | # Tuxis Internet Engineering V.O.F. , 2016 8 | # Tuxis Internet Engineering V.O.F. , 2016 9 | msgid "" 10 | msgstr "" 11 | "Project-Id-Version: Modoboa\n" 12 | "Report-Msgid-Bugs-To: \n" 13 | "POT-Creation-Date: 2020-05-06 14:05+0200\n" 14 | "PO-Revision-Date: 2017-03-03 08:18+0000\n" 15 | "Last-Translator: Antoine Nguyen \n" 16 | "Language-Team: Dutch (Netherlands) (http://www.transifex.com/tonio/modoboa/" 17 | "language/nl_NL/)\n" 18 | "Language: nl_NL\n" 19 | "MIME-Version: 1.0\n" 20 | "Content-Type: text/plain; charset=UTF-8\n" 21 | "Content-Transfer-Encoding: 8bit\n" 22 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 23 | 24 | #: forms.py:35 25 | msgid "DNS settings" 26 | msgstr "" 27 | 28 | #: forms.py:38 29 | msgid "Enable reverse lookups" 30 | msgstr "" 31 | 32 | #: forms.py:41 33 | msgid "Enable reverse DNS lookups (reports will be longer to display)" 34 | msgstr "" 35 | 36 | #: handlers.py:20 37 | msgid "Show DMARC report for {}" 38 | msgstr "Toon DMARC rapportage voor {}" 39 | 40 | #: models.py:10 models.py:16 41 | msgid "None" 42 | msgstr "Geen" 43 | 44 | #: models.py:11 45 | msgid "Quarantine" 46 | msgstr "Quarantaine" 47 | 48 | #: models.py:12 49 | msgid "Reject" 50 | msgstr "Afkeuren" 51 | 52 | #: models.py:17 53 | msgid "Neutral" 54 | msgstr "Neutraal" 55 | 56 | #: models.py:18 57 | msgid "Pass" 58 | msgstr "Gelukt" 59 | 60 | #: models.py:19 61 | msgid "Fail" 62 | msgstr "Mislukt" 63 | 64 | #: models.py:20 65 | msgid "Temporary error" 66 | msgstr "Tijdelijke fout" 67 | 68 | #: models.py:21 69 | msgid "Permanent error" 70 | msgstr "Permanente fout" 71 | 72 | #: models.py:25 73 | msgid "Policy" 74 | msgstr "Beleid" 75 | 76 | #: models.py:29 77 | msgid "Soft failure" 78 | msgstr "Toegestane fout" 79 | 80 | #: modo_extension.py:16 81 | msgid "DMARC tools" 82 | msgstr "DMARC gereedschappen" 83 | 84 | #: modo_extension.py:19 85 | msgid "A set of tools to ease DMARC integration" 86 | msgstr "Een aantal eenvoudige programmas DMARC" 87 | 88 | #: templates/modoboa_dmarc/_result_detail.html:6 89 | msgid "Total" 90 | msgstr "Totaal" 91 | 92 | #: templates/modoboa_dmarc/domain_report.html:21 93 | #: templates/modoboa_dmarc/domain_report.html:26 94 | msgid "Fully aligned" 95 | msgstr "Volledig alligned" 96 | 97 | #: templates/modoboa_dmarc/domain_report.html:22 98 | #: templates/modoboa_dmarc/domain_report.html:27 99 | msgid "Partially aligned" 100 | msgstr "Gedeeltelijk alligned" 101 | 102 | #: templates/modoboa_dmarc/domain_report.html:23 103 | #: templates/modoboa_dmarc/domain_report.html:28 104 | msgid "Failed" 105 | msgstr "Mislukt" 106 | 107 | #: templates/modoboa_dmarc/domain_report.html:50 108 | msgid "DMARC report for" 109 | msgstr "DMARC rapportage voor" 110 | 111 | #: templates/modoboa_dmarc/domain_report.html:74 112 | msgid "processed" 113 | msgstr "verwerkt" 114 | 115 | #: templates/modoboa_dmarc/domain_report.html:83 116 | msgid "fully aligned" 117 | msgstr "volledig alligned" 118 | 119 | #: templates/modoboa_dmarc/domain_report.html:92 120 | msgid "partially aligned" 121 | msgstr "gedeeltelijk alligned" 122 | 123 | #: templates/modoboa_dmarc/domain_report.html:101 124 | msgid "failed" 125 | msgstr "mislukt" 126 | 127 | #: templates/modoboa_dmarc/domain_report.html:111 128 | msgid "Display details" 129 | msgstr "Toon details" 130 | 131 | #: templates/modoboa_dmarc/domain_report.html:115 132 | msgid "Trusted sources" 133 | msgstr "Vertrouwde bronnen" 134 | 135 | #: templates/modoboa_dmarc/domain_report.html:123 136 | msgid "Unknown sources / Threats" 137 | msgstr "Onbekende bronnen / bedreigingen" 138 | 139 | #: templatetags/dmarc_tags.py:59 views.py:120 140 | msgid "Not resolved" 141 | msgstr "" 142 | -------------------------------------------------------------------------------- /modoboa_dmarc/locale/pt_BR/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # 5 | # Translators: 6 | # Paulino Michelazzo , 2015 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: Modoboa\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2020-05-06 14:05+0200\n" 12 | "PO-Revision-Date: 2017-03-03 08:18+0000\n" 13 | "Last-Translator: Antoine Nguyen \n" 14 | "Language-Team: Portuguese (Brazil) (http://www.transifex.com/tonio/modoboa/" 15 | "language/pt_BR/)\n" 16 | "Language: pt_BR\n" 17 | "MIME-Version: 1.0\n" 18 | "Content-Type: text/plain; charset=UTF-8\n" 19 | "Content-Transfer-Encoding: 8bit\n" 20 | "Plural-Forms: nplurals=2; plural=(n > 1);\n" 21 | 22 | #: forms.py:35 23 | msgid "DNS settings" 24 | msgstr "" 25 | 26 | #: forms.py:38 27 | msgid "Enable reverse lookups" 28 | msgstr "" 29 | 30 | #: forms.py:41 31 | msgid "Enable reverse DNS lookups (reports will be longer to display)" 32 | msgstr "" 33 | 34 | #: handlers.py:20 35 | msgid "Show DMARC report for {}" 36 | msgstr "Mostrar relatório DMARC para {}" 37 | 38 | #: models.py:10 models.py:16 39 | msgid "None" 40 | msgstr "Nenhum" 41 | 42 | #: models.py:11 43 | msgid "Quarantine" 44 | msgstr "Quarentena" 45 | 46 | #: models.py:12 47 | msgid "Reject" 48 | msgstr "Rejeitado" 49 | 50 | #: models.py:17 51 | msgid "Neutral" 52 | msgstr "Neutro" 53 | 54 | #: models.py:18 55 | msgid "Pass" 56 | msgstr "Passou" 57 | 58 | #: models.py:19 59 | msgid "Fail" 60 | msgstr "Falha" 61 | 62 | #: models.py:20 63 | msgid "Temporary error" 64 | msgstr "Erro temporário" 65 | 66 | #: models.py:21 67 | msgid "Permanent error" 68 | msgstr "Erro permanente" 69 | 70 | #: models.py:25 71 | msgid "Policy" 72 | msgstr "Política" 73 | 74 | #: models.py:29 75 | msgid "Soft failure" 76 | msgstr "Falha leve" 77 | 78 | #: modo_extension.py:16 79 | msgid "DMARC tools" 80 | msgstr "Ferramentas DMARC" 81 | 82 | #: modo_extension.py:19 83 | msgid "A set of tools to ease DMARC integration" 84 | msgstr "Um conjunto de ferramentas para fácil integração do DMARC" 85 | 86 | #: templates/modoboa_dmarc/_result_detail.html:6 87 | msgid "Total" 88 | msgstr "Total" 89 | 90 | #: templates/modoboa_dmarc/domain_report.html:21 91 | #: templates/modoboa_dmarc/domain_report.html:26 92 | msgid "Fully aligned" 93 | msgstr "Completamente alinhado" 94 | 95 | #: templates/modoboa_dmarc/domain_report.html:22 96 | #: templates/modoboa_dmarc/domain_report.html:27 97 | msgid "Partially aligned" 98 | msgstr "Parcialmente alinhado" 99 | 100 | #: templates/modoboa_dmarc/domain_report.html:23 101 | #: templates/modoboa_dmarc/domain_report.html:28 102 | msgid "Failed" 103 | msgstr "Falha" 104 | 105 | #: templates/modoboa_dmarc/domain_report.html:50 106 | msgid "DMARC report for" 107 | msgstr "Relatório DMARC para" 108 | 109 | #: templates/modoboa_dmarc/domain_report.html:74 110 | msgid "processed" 111 | msgstr "processado" 112 | 113 | #: templates/modoboa_dmarc/domain_report.html:83 114 | msgid "fully aligned" 115 | msgstr "completamente alinhado" 116 | 117 | #: templates/modoboa_dmarc/domain_report.html:92 118 | msgid "partially aligned" 119 | msgstr "parcialmente alinhado" 120 | 121 | #: templates/modoboa_dmarc/domain_report.html:101 122 | msgid "failed" 123 | msgstr "falho" 124 | 125 | #: templates/modoboa_dmarc/domain_report.html:111 126 | msgid "Display details" 127 | msgstr "Mostrar detalhes" 128 | 129 | #: templates/modoboa_dmarc/domain_report.html:115 130 | msgid "Trusted sources" 131 | msgstr "Fontes confiáveis" 132 | 133 | #: templates/modoboa_dmarc/domain_report.html:123 134 | msgid "Unknown sources / Threats" 135 | msgstr "Fontes desconhecidas / Ameaças" 136 | 137 | #: templatetags/dmarc_tags.py:59 views.py:120 138 | msgid "Not resolved" 139 | msgstr "" 140 | -------------------------------------------------------------------------------- /modoboa_dmarc/locale/sv/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # 5 | # Translators: 6 | # Olle Gustafsson , 2016 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: Modoboa\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2020-05-06 14:05+0200\n" 12 | "PO-Revision-Date: 2017-03-03 08:18+0000\n" 13 | "Last-Translator: Antoine Nguyen \n" 14 | "Language-Team: Swedish (http://www.transifex.com/tonio/modoboa/language/" 15 | "sv/)\n" 16 | "Language: sv\n" 17 | "MIME-Version: 1.0\n" 18 | "Content-Type: text/plain; charset=UTF-8\n" 19 | "Content-Transfer-Encoding: 8bit\n" 20 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 21 | 22 | #: forms.py:35 23 | msgid "DNS settings" 24 | msgstr "" 25 | 26 | #: forms.py:38 27 | msgid "Enable reverse lookups" 28 | msgstr "" 29 | 30 | #: forms.py:41 31 | msgid "Enable reverse DNS lookups (reports will be longer to display)" 32 | msgstr "" 33 | 34 | #: handlers.py:20 35 | msgid "Show DMARC report for {}" 36 | msgstr "Visa DMARC rapport för {}" 37 | 38 | #: models.py:10 models.py:16 39 | msgid "None" 40 | msgstr "Inga" 41 | 42 | #: models.py:11 43 | msgid "Quarantine" 44 | msgstr "Karantän" 45 | 46 | #: models.py:12 47 | msgid "Reject" 48 | msgstr "Avvisa" 49 | 50 | #: models.py:17 51 | msgid "Neutral" 52 | msgstr "Netural" 53 | 54 | #: models.py:18 55 | msgid "Pass" 56 | msgstr "Passera" 57 | 58 | #: models.py:19 59 | msgid "Fail" 60 | msgstr "Avslå" 61 | 62 | #: models.py:20 63 | msgid "Temporary error" 64 | msgstr "Tillfälligt fel" 65 | 66 | #: models.py:21 67 | msgid "Permanent error" 68 | msgstr "Permanent fel" 69 | 70 | #: models.py:25 71 | msgid "Policy" 72 | msgstr "Policy" 73 | 74 | #: models.py:29 75 | msgid "Soft failure" 76 | msgstr "Mjukt misslyckande" 77 | 78 | #: modo_extension.py:16 79 | msgid "DMARC tools" 80 | msgstr "DMARC verktyg" 81 | 82 | #: modo_extension.py:19 83 | msgid "A set of tools to ease DMARC integration" 84 | msgstr "En uppsättning verktyg för att underlätta DMARC integration" 85 | 86 | #: templates/modoboa_dmarc/_result_detail.html:6 87 | msgid "Total" 88 | msgstr "Totalt" 89 | 90 | #: templates/modoboa_dmarc/domain_report.html:21 91 | #: templates/modoboa_dmarc/domain_report.html:26 92 | msgid "Fully aligned" 93 | msgstr "Helt i linje" 94 | 95 | #: templates/modoboa_dmarc/domain_report.html:22 96 | #: templates/modoboa_dmarc/domain_report.html:27 97 | msgid "Partially aligned" 98 | msgstr "Delvis i linje" 99 | 100 | #: templates/modoboa_dmarc/domain_report.html:23 101 | #: templates/modoboa_dmarc/domain_report.html:28 102 | msgid "Failed" 103 | msgstr "Misslyckades" 104 | 105 | #: templates/modoboa_dmarc/domain_report.html:50 106 | msgid "DMARC report for" 107 | msgstr "DMARC rapport för" 108 | 109 | #: templates/modoboa_dmarc/domain_report.html:74 110 | msgid "processed" 111 | msgstr "bearbetats" 112 | 113 | #: templates/modoboa_dmarc/domain_report.html:83 114 | msgid "fully aligned" 115 | msgstr "helt i linje" 116 | 117 | #: templates/modoboa_dmarc/domain_report.html:92 118 | msgid "partially aligned" 119 | msgstr "delvis i linje" 120 | 121 | #: templates/modoboa_dmarc/domain_report.html:101 122 | msgid "failed" 123 | msgstr "misslyckades" 124 | 125 | #: templates/modoboa_dmarc/domain_report.html:111 126 | msgid "Display details" 127 | msgstr "Visa detaljer" 128 | 129 | #: templates/modoboa_dmarc/domain_report.html:115 130 | msgid "Trusted sources" 131 | msgstr "Betrodda källor" 132 | 133 | #: templates/modoboa_dmarc/domain_report.html:123 134 | msgid "Unknown sources / Threats" 135 | msgstr "Okända källor / Hot" 136 | 137 | #: templatetags/dmarc_tags.py:59 views.py:120 138 | msgid "Not resolved" 139 | msgstr "" 140 | -------------------------------------------------------------------------------- /modoboa_dmarc/management/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modoboa/modoboa-dmarc/5b8ac71be1787b9d9d71c447d335e63b86fd4cf6/modoboa_dmarc/management/__init__.py -------------------------------------------------------------------------------- /modoboa_dmarc/management/commands/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modoboa/modoboa-dmarc/5b8ac71be1787b9d9d71c447d335e63b86fd4cf6/modoboa_dmarc/management/commands/__init__.py -------------------------------------------------------------------------------- /modoboa_dmarc/management/commands/import_aggregated_report.py: -------------------------------------------------------------------------------- 1 | """Import a DMARC aggregated report.""" 2 | 3 | from __future__ import print_function 4 | 5 | from django.core.management.base import BaseCommand 6 | 7 | from ... import lib 8 | 9 | 10 | class Command(BaseCommand): 11 | """Command definition.""" 12 | 13 | help = "Import DMARC aggregated reports." 14 | 15 | def add_arguments(self, parser): 16 | """Add extra arguments to parser.""" 17 | parser.add_argument( 18 | "--pipe", action="store_true", default=False, 19 | help="Import a report from an email given on stdin") 20 | parser.add_argument( 21 | "--imap", action="store_true", default=False, 22 | help="Import a report from an IMAP mailbox") 23 | parser.add_argument( 24 | "--host", default="localhost", 25 | help="IMAP host") 26 | parser.add_argument( 27 | "--ssl", action="store_true", default=False, 28 | help="Connect using SSL"), 29 | parser.add_argument( 30 | "--mailbox", default="INBOX", 31 | help="IMAP mailbox to import reports from") 32 | 33 | def handle(self, *args, **options): 34 | """Entry point.""" 35 | if options.get("pipe"): 36 | lib.import_report_from_stdin() 37 | elif options.get("imap"): 38 | lib.import_from_imap(options) 39 | else: 40 | print("Nothing to do.") 41 | -------------------------------------------------------------------------------- /modoboa_dmarc/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('admin', '0002_migrate_from_modoboa_admin'), 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name='Record', 16 | fields=[ 17 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), 18 | ('source_ip', models.GenericIPAddressField()), 19 | ('count', models.IntegerField()), 20 | ('disposition', models.CharField(max_length=10, choices=[(b'none', 'None'), (b'quarantine', 'Quarantine'), (b'reject', 'Reject')])), 21 | ('dkim_result', models.CharField(max_length=9, choices=[(b'none', 'None'), (b'neutral', 'Neutral'), (b'pass', 'Pass'), (b'fail', 'Fail'), (b'temperror', 'Temporary error'), (b'permerror', 'Permanent error'), (b'policy', 'Policy')])), 22 | ('spf_result', models.CharField(max_length=9, choices=[(b'none', 'None'), (b'neutral', 'Neutral'), (b'pass', 'Pass'), (b'fail', 'Fail'), (b'temperror', 'Temporary error'), (b'permerror', 'Permanent error'), (b'softfail', 'Soft failure')])), 23 | ('reason_type', models.CharField(max_length=15, blank=True)), 24 | ('reason_comment', models.CharField(max_length=100, blank=True)), 25 | ('header_from', models.ForeignKey(to='admin.Domain', on_delete=models.CASCADE)), 26 | ], 27 | options={ 28 | }, 29 | bases=(models.Model,), 30 | ), 31 | migrations.CreateModel( 32 | name='Report', 33 | fields=[ 34 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), 35 | ('report_id', models.CharField(max_length=100)), 36 | ('start_date', models.DateTimeField()), 37 | ('end_date', models.DateTimeField()), 38 | ('policy_domain', models.CharField(max_length=100)), 39 | ('policy_adkim', models.CharField(max_length=1)), 40 | ('policy_aspf', models.CharField(max_length=1)), 41 | ('policy_p', models.CharField(max_length=10)), 42 | ('policy_sp', models.CharField(max_length=10)), 43 | ('policy_pct', models.SmallIntegerField()), 44 | ], 45 | options={ 46 | 'permissions': (('view_report', 'Can view report'),), 47 | }, 48 | bases=(models.Model,), 49 | ), 50 | migrations.CreateModel( 51 | name='Reporter', 52 | fields=[ 53 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), 54 | ('org_name', models.CharField(max_length=100)), 55 | ('email', models.EmailField(unique=True, max_length=254)), 56 | ], 57 | options={ 58 | }, 59 | bases=(models.Model,), 60 | ), 61 | migrations.CreateModel( 62 | name='Result', 63 | fields=[ 64 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), 65 | ('type', models.CharField(max_length=4, choices=[(b'dkim', b'DKIM'), (b'spf', b'SPF')])), 66 | ('domain', models.CharField(max_length=100)), 67 | ('result', models.CharField(max_length=9)), 68 | ('record', models.ForeignKey(to='modoboa_dmarc.Record', on_delete=models.CASCADE)), 69 | ], 70 | options={ 71 | }, 72 | bases=(models.Model,), 73 | ), 74 | migrations.AddField( 75 | model_name='report', 76 | name='reporter', 77 | field=models.ForeignKey(to='modoboa_dmarc.Reporter', on_delete=models.CASCADE), 78 | preserve_default=True, 79 | ), 80 | migrations.AlterUniqueTogether( 81 | name='report', 82 | unique_together=set([('reporter', 'report_id')]), 83 | ), 84 | migrations.AddField( 85 | model_name='record', 86 | name='report', 87 | field=models.ForeignKey(to='modoboa_dmarc.Report', on_delete=models.CASCADE), 88 | preserve_default=True, 89 | ), 90 | ] 91 | -------------------------------------------------------------------------------- /modoboa_dmarc/migrations/0002_auto_20180124_2311.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.8 on 2018-01-24 23:11 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('modoboa_dmarc', '0001_initial'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='record', 17 | name='disposition', 18 | field=models.CharField(choices=[('none', 'None'), ('quarantine', 'Quarantine'), ('reject', 'Reject')], max_length=10), 19 | ), 20 | migrations.AlterField( 21 | model_name='record', 22 | name='dkim_result', 23 | field=models.CharField(choices=[('none', 'None'), ('neutral', 'Neutral'), ('pass', 'Pass'), ('fail', 'Fail'), ('temperror', 'Temporary error'), ('permerror', 'Permanent error'), ('policy', 'Policy')], max_length=9), 24 | ), 25 | migrations.AlterField( 26 | model_name='record', 27 | name='spf_result', 28 | field=models.CharField(choices=[('none', 'None'), ('neutral', 'Neutral'), ('pass', 'Pass'), ('fail', 'Fail'), ('temperror', 'Temporary error'), ('permerror', 'Permanent error'), ('softfail', 'Soft failure')], max_length=9), 29 | ), 30 | migrations.AlterField( 31 | model_name='result', 32 | name='type', 33 | field=models.CharField(choices=[('dkim', 'DKIM'), ('spf', 'SPF')], max_length=4), 34 | ), 35 | ] 36 | -------------------------------------------------------------------------------- /modoboa_dmarc/migrations/0003_auto_20200227_0936.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.9 on 2020-02-27 08:36 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('modoboa_dmarc', '0002_auto_20180124_2311'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterModelOptions( 14 | name='report', 15 | options={}, 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /modoboa_dmarc/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modoboa/modoboa-dmarc/5b8ac71be1787b9d9d71c447d335e63b86fd4cf6/modoboa_dmarc/migrations/__init__.py -------------------------------------------------------------------------------- /modoboa_dmarc/models.py: -------------------------------------------------------------------------------- 1 | """DMARC models.""" 2 | 3 | from django.db import models 4 | from django.utils.translation import ugettext_lazy as _ 5 | 6 | from modoboa.admin import models as admin_models 7 | 8 | 9 | POLICY_DISPOSITIONS = [ 10 | ("none", _("None")), 11 | ("quarantine", _("Quarantine")), 12 | ("reject", _("Reject")) 13 | ] 14 | 15 | COMMON_RESULTS = [ 16 | ("none", _("None")), 17 | ("neutral", _("Neutral")), 18 | ("pass", _("Pass")), 19 | ("fail", _("Fail")), 20 | ("temperror", _("Temporary error")), 21 | ("permerror", _("Permanent error")) 22 | ] 23 | 24 | DKIM_RESULTS = COMMON_RESULTS + [ 25 | ("policy", _("Policy")) 26 | ] 27 | 28 | SPF_RESULTS = COMMON_RESULTS + [ 29 | ("softfail", _("Soft failure")) 30 | ] 31 | 32 | RECORD_TYPES = [ 33 | ("dkim", "DKIM"), 34 | ("spf", "SPF"), 35 | ] 36 | 37 | 38 | class Reporter(models.Model): 39 | 40 | """Report issuers.""" 41 | 42 | org_name = models.CharField(max_length=100) 43 | email = models.EmailField(unique=True) 44 | 45 | def __str__(self): 46 | """Return name and email.""" 47 | return "{} <{}>".format(self.org_name, self.email) 48 | 49 | 50 | class Report(models.Model): 51 | 52 | """Aggregated reports.""" 53 | 54 | report_id = models.CharField(max_length=100) 55 | reporter = models.ForeignKey(Reporter, on_delete=models.CASCADE) 56 | start_date = models.DateTimeField() 57 | end_date = models.DateTimeField() 58 | 59 | # Published policy 60 | policy_domain = models.CharField(max_length=100) 61 | policy_adkim = models.CharField(max_length=1) 62 | policy_aspf = models.CharField(max_length=1) 63 | policy_p = models.CharField(max_length=10) 64 | policy_sp = models.CharField(max_length=10) 65 | policy_pct = models.SmallIntegerField() 66 | 67 | class Meta: 68 | unique_together = ("reporter", "report_id") 69 | 70 | def __str__(self): 71 | """Display provider and dates.""" 72 | return "{}: {} -> {}".format( 73 | self.reporter, self.start_date, self.end_date) 74 | 75 | 76 | class Record(models.Model): 77 | 78 | """Report records.""" 79 | 80 | report = models.ForeignKey(Report, on_delete=models.CASCADE) 81 | source_ip = models.GenericIPAddressField() 82 | count = models.IntegerField() 83 | 84 | # Policy evaluated 85 | disposition = models.CharField( 86 | max_length=10, choices=POLICY_DISPOSITIONS) 87 | dkim_result = models.CharField( 88 | max_length=9, choices=DKIM_RESULTS) 89 | spf_result = models.CharField( 90 | max_length=9, choices=SPF_RESULTS) 91 | header_from = models.ForeignKey(admin_models.Domain, 92 | on_delete=models.CASCADE) 93 | reason_type = models.CharField(max_length=15, blank=True) 94 | reason_comment = models.CharField(max_length=100, blank=True) 95 | 96 | 97 | class Result(models.Model): 98 | 99 | """Record results.""" 100 | 101 | record = models.ForeignKey(Record, on_delete=models.CASCADE) 102 | type = models.CharField(max_length=4, choices=RECORD_TYPES) 103 | domain = models.CharField(max_length=100) 104 | result = models.CharField(max_length=9) 105 | -------------------------------------------------------------------------------- /modoboa_dmarc/modo_extension.py: -------------------------------------------------------------------------------- 1 | """DMARC tools for Modoboa.""" 2 | 3 | from django.utils.translation import ugettext_lazy 4 | 5 | from modoboa.core.extensions import ModoExtension, exts_pool 6 | from modoboa.parameters import tools as param_tools 7 | 8 | from . import __version__ 9 | from . import forms 10 | 11 | 12 | class DmarcExtension(ModoExtension): 13 | """Extension registration.""" 14 | 15 | name = "modoboa_dmarc" 16 | label = ugettext_lazy("DMARC tools") 17 | version = __version__ 18 | description = ugettext_lazy( 19 | "A set of tools to ease DMARC integration" 20 | ) 21 | url = "dmarc" 22 | 23 | def load(self): 24 | """Extension loading.""" 25 | param_tools.registry.add("global", forms.ParametersForm, "DMARC") 26 | 27 | 28 | exts_pool.register_extension(DmarcExtension) 29 | -------------------------------------------------------------------------------- /modoboa_dmarc/templates/modoboa_dmarc/_result_detail.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | 3 | 4 | {{ domain }} 5 | {% if forloop.first %} 6 | {% trans "Total" %} 7 | SPF 8 | DKIM 9 | {% else %} 10 | 11 | {% endif %} 12 | 13 | 14 | {% for ip_address, results in stats.items %} 15 | 16 | {{ ip_address }} 17 | {{ results.total }} 18 | 19 | {% if not only_failed %}{{ results.spf.pass }}{% endif %} 20 | {{ results.spf.fail }} 21 | 22 | 23 | {% if not only_failed %}{{ results.dkim.pass }}{% endif %} 24 | {{ results.dkim.fail }} 25 | 26 | 27 | {% endfor %} 28 | -------------------------------------------------------------------------------- /modoboa_dmarc/templates/modoboa_dmarc/domain_report.html: -------------------------------------------------------------------------------- 1 | {% extends "admin/domains.html" %} 2 | 3 | {% load i18n l10n %} 4 | {% load static %} 5 | {% load admin_tags %} 6 | {% load dmarc_tags %} 7 | 8 | {% block extra_css %} 9 | 10 | {% endblock %} 11 | 12 | {% block extra_js %} 13 | 14 | 15 | 41 | {% endblock %} 42 | 43 | {% block leftcol %} 44 | 47 | {% endblock %} 48 | 49 | {% block apparea %} 50 |
51 |
52 |

{% trans "DMARC report for" %} {{ domain }}

53 |
54 |
55 |
56 |
57 | 58 | {{ daterange.0|date }} - {{ daterange.1|date }} 59 | 60 |
61 |
62 |
63 |
64 | 65 |
66 |
67 |
68 |
69 | 70 |
71 |
72 |
73 |
74 |
75 |

{{ stats.total }}

76 |

{% trans "processed" %}

77 |
78 |
79 |
80 | 81 |
82 |
83 |
84 |

{{ stats.aligned }}

85 |

{% trans "fully aligned" %}

86 |
87 |
88 |
89 | 90 |
91 |
92 |
93 |

{{ stats.trusted }}

94 |

{% trans "partially aligned" %}

95 |
96 |
97 |
98 | 99 |
100 |
101 |
102 |

{{ stats.forwarded }}

103 |

{% trans "forwarded" %}

104 |
105 |
106 |
107 | 108 |
109 |
110 |
111 |

{{ stats.failed }}

112 |

{% trans "failed" %}

113 |
114 |
115 |
116 |
117 |
118 |
119 | 120 |
121 | 123 |
124 | 125 | 158 | 159 | {% endblock %} 160 | -------------------------------------------------------------------------------- /modoboa_dmarc/templatetags/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modoboa/modoboa-dmarc/5b8ac71be1787b9d9d71c447d335e63b86fd4cf6/modoboa_dmarc/templatetags/__init__.py -------------------------------------------------------------------------------- /modoboa_dmarc/templatetags/dmarc_tags.py: -------------------------------------------------------------------------------- 1 | """Custom template tags.""" 2 | 3 | import datetime 4 | 5 | from collections import OrderedDict 6 | 7 | from django import template 8 | from django.utils.safestring import mark_safe 9 | from django.utils.translation import ugettext_lazy as _ 10 | 11 | register = template.Library() 12 | 13 | 14 | @register.simple_tag 15 | def next_period(period): 16 | """Return next period.""" 17 | current = period.split("-") 18 | if int(current[1]) == 52: 19 | week0 = datetime.datetime.strptime("{}-0-1".format(int(current[0]) + 1 ), "%Y-%W-%w") 20 | week1 = datetime.datetime.strptime("{}-1-1".format(int(current[0]) + 1 ), "%Y-%W-%w") 21 | week52 = datetime.datetime.strptime("{}-52-1".format(current[0]), "%Y-%W-%w") 22 | if week0 == week52 or week0 == week1 : 23 | parts = (int(current[0]) + 1, 1 ) 24 | else: 25 | parts = (int(current[0]) + 1, 0 ) 26 | else: 27 | parts = (current[0], int(current[1]) + 1 ) 28 | return mark_safe("{}-{}".format(parts[0], parts[1])) 29 | 30 | @register.simple_tag 31 | def previous_period(period): 32 | """Return previous period.""" 33 | current = period.split("-") 34 | if int(current[1]) == 1: 35 | week0 = datetime.datetime.strptime("{}-0-1".format(current[0]), "%Y-%W-%w") 36 | week1 = datetime.datetime.strptime("{}-1-1".format(current[0]), "%Y-%W-%w") 37 | if week0 == week1: 38 | parts = (int(current[0]) - 1, 52 ) 39 | else: 40 | parts = (current[0], 0 ) 41 | elif int(current[1]) == 0: 42 | week0 = datetime.datetime.strptime("{}-0-1".format(current[0]), "%Y-%W-%w") 43 | week52 = datetime.datetime.strptime("{}-52-1".format(int(current[0]) - 1 ), "%Y-%W-%w") 44 | if week0 == week52: 45 | parts = (int(current[0]) - 1, 51 ) 46 | else: 47 | parts = (int(current[0]) - 1, 52 ) 48 | else: 49 | parts = (current[0], int(current[1]) - 1 ) 50 | return mark_safe("{}-{}".format(parts[0], parts[1])) 51 | 52 | 53 | @register.filter 54 | def domain_sorted_items(domain_dict): 55 | """Return a list of tuples ordered alphabetically by domain names.""" 56 | if isinstance(domain_dict, dict): 57 | sorted_domain_dict = OrderedDict( 58 | sorted(domain_dict.items(), key=lambda t: t[0])) 59 | unresolved_label = _("Not resolved") 60 | unresolved = sorted_domain_dict.pop(unresolved_label, None) 61 | if unresolved: 62 | sorted_domain_dict[unresolved_label] = unresolved 63 | return sorted_domain_dict.items() 64 | 65 | raise ValueError("domain_dict is not a dict") 66 | -------------------------------------------------------------------------------- /modoboa_dmarc/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modoboa/modoboa-dmarc/5b8ac71be1787b9d9d71c447d335e63b86fd4cf6/modoboa_dmarc/tests/__init__.py -------------------------------------------------------------------------------- /modoboa_dmarc/tests/fail-reports/Report_Domain_ngyn.org_Submitter_163.com_Report-ID_aggr_report_ngyn.org_20150705_163-failed.com.eml: -------------------------------------------------------------------------------- 1 | Return-Path: 2 | Delivered-To: 3 | Received: from mail.koalabs.org 4 | by nelson.ngyn.org (Dovecot) with LMTP id IKPdD1ULmlXgLQAABvoInA 5 | for ; Mon, 06 Jul 2015 07:00:05 +0200 6 | Received: from localhost (localhost.localdomain [127.0.0.1]) 7 | by mail.koalabs.org (Postfix) with ESMTP id 2645EE035B 8 | for ; Mon, 6 Jul 2015 07:00:05 +0200 (CEST) 9 | X-Virus-Scanned: Debian amavisd-new at mail.koalabs.org 10 | Received: from mail.koalabs.org ([127.0.0.1]) 11 | by localhost (nelson.ngyn.org [127.0.0.1]) (amavisd-new, port 10024) 12 | with ESMTP for ; 13 | Mon, 6 Jul 2015 07:00:03 +0200 (CEST) 14 | Received: from m12-181.163.com (m12-181.163.com [220.181.12.181]) 15 | by mail.koalabs.org (Postfix) with ESMTP 16 | for ; Mon, 6 Jul 2015 06:59:54 +0200 (CEST) 17 | Received: from 163.com (unknown [192.168.201.141]) 18 | by mfast1 (Coremail) with SMTP id tcCowEAJixEvC5pVHJvvLg--.53754S2; 19 | Mon, 06 Jul 2015 12:59:27 +0800 (CST) 20 | Content-Type: multipart/mixed; boundary="===============2544031943362770105==" 21 | MIME-Version: 1.0 22 | From: 23 | Date: Mon, 06 Jul 2015 12:59:25 +0800 24 | Subject: Report Domain: ngyn.org Submitter: 163.com Report-ID: 25 | aggr_report_ngyn.org_20150705_163.com 26 | To: postmaster@ngyn.org 27 | X-CM-TRANSID:tcCowEAJixEvC5pVHJvvLg--.53754S2 28 | Message-Id:<559A0B30.EBEE1F.29746@m12-181.163.com> 29 | X-Coremail-Antispam: 1UD129KBjDUn29KB7ZKAUJUUUUU529EdanIXcx71UUUUU7v73 30 | VFW2AGmfu7bjvjm3AaLaJ3UjIYCTnIWjp_UUUnI7CY07I20VC2zVCF04k26cxKx2IYs7xG 31 | 6rWj6s0DM28lY4IEw2IIxxk0rwA2F7IY1VAKz4vEj48ve4kI8wAa7VASzI0EjI02j7AqF2 32 | xKxVCjxxvEa2IrM2AIxVAIcxkEcVAq07x20xvEncxIr21le4C267I2x7xF54xIwI1l5I8C 33 | rVACY4xI64kE6c02F40Ex7xfMcIj6x8ErcxFaVAv8VWrMcvjeVCFs4IE7xkEbVWUJVW8Jw 34 | ACjcxG0xvEwIxGrwAKzVAC0xCFj2AI6cx7M4kE6xkIj40Ew7xC0wCjxxvEa2IrMxkIecxE 35 | wVCm-wCY0x0Ix7I2Y4AK64vIr41l42xK82IYc2Ij64vIr41l42xK82IY6x8ErcxFaVAv8V 36 | WrMxCjnVAqn7xvrwCFx2IqxVCFs4IE7xkEbVWUJVW8JwC20s026c02F40E14v26r1j6r18 37 | MI8I3I0E7480Y4vE14v26r106r1rMI8E67AF67kF1VAFwI0_Jr0_JrylIxkGc2Ij64vIr4 38 | 1l6VACY4xI67k04243AbIYCTnIWIevJa73UjIFyTuYvjTRClkVDUUUU 39 | X-Originating-IP: [192.168.201.141] 40 | X-CM-SenderInfo: 5dex2vi6rwjhhfrp/ 41 | 42 | --===============2544031943362770105== 43 | Content-Type: text/plain; charset="us-ascii" 44 | MIME-Version: 1.0 45 | Content-Transfer-Encoding: 7bit 46 | 47 | This is a DMARC aggregate report for domain ngyn.org on 20150705. For more information please mail to abuse@163.com. 48 | --===============2544031943362770105== 49 | MIME-Version: 1.0 50 | Content-Type: application/gzip; 51 | name="163.com!ngyn.org!1436054400!1436140799.zip" 52 | Content-Transfer-Encoding: base64 53 | Content-Disposition: attachment; 54 | filename="163.com!ngyn.org!1436054400!1436140799.zip" 55 | 56 | UEsDBBQAAAAIAGxn5kZGEGLBdAEAAJQDAAAqAAAAMTYzLmNvbSFuZ3luLm9yZyExNDM2MDU0NDAw 57 | ITE0MzYxNDA3OTkueG1sbVNbbsMgEPzvKXICg5M4USSEehOEzdpBsQEBbpXbF4eHadovzOzs7Owu 58 | JiOA6PnwoB+HA7FgtPVsAc8F93zDAqrtxBRfgLaXUzPohaCCRAIsXM6U96uDz8KJYCQkXSkonybL 59 | 0lVNT9VsUkfcdviKO1aS94QoENwAs1xNqWSAepikou35dMHd+YwxQRHJcVDiFW3P+Hq7BT8qi6Hf 60 | aqVa3TYxepbDk5m1n6W7QzGiQ1uKZutBKwIxysVDLtQRFD8S6Mz4wrYzQoYqrYAgk+4uAy4jZvC0 61 | C4RwvCz+ZyeMddA2O7P6u/Tu9GoHYNLQtu2aIz42l3CcguAeydxBr8rTlqD4keFUEL74vIZxiRzY 62 | ZiCd0U56qVWyXSMVb5vBGF5BIJRxpH7HFCgzqZp8qxn2k1sjUoDycpRgXUm7Axdg2Wj1Qtdm30yN 63 | J6E/6YSv/h7eo1tnvytWpvaV19L10tMD3xTSNNKltLXvHb2X28hxhwSVP/EHUEsBAhQDFAAAAAgA 64 | bGfmRkYQYsF0AQAAlAMAACoAAAAAAAAAAAAAAKSBAAAAADE2My5jb20hbmd5bi5vcmchMTQzNjA1 65 | NDQwMCExNDM2MTQwNzk5LnhtbFBLBQYAAAAAAQABAFgAAAC8AQAAAAA= 66 | --===============2544031943362770105==-- 67 | 68 | -------------------------------------------------------------------------------- /modoboa_dmarc/tests/mixins.py: -------------------------------------------------------------------------------- 1 | """Test mixins.""" 2 | 3 | import io 4 | import os 5 | import sys 6 | 7 | from django.core.management import call_command 8 | 9 | 10 | class CallCommandMixin(object): 11 | """A mixin to provide command execution shortcuts.""" 12 | 13 | def setUp(self): 14 | """Replace stdin""" 15 | super(CallCommandMixin, self).setUp() 16 | self.stdin = sys.stdin 17 | 18 | def tearDown(self): 19 | sys.stdin = self.stdin 20 | 21 | def import_report(self, path): 22 | """Import test report from file.""" 23 | with open(path) as fp: 24 | buf = io.StringIO(fp.read()) 25 | sys.stdin = buf 26 | out = io.StringIO() 27 | call_command("import_aggregated_report", "--pipe", stdout=out) 28 | return out.getvalue() 29 | 30 | def import_reports(self, folder="reports"): 31 | """Import reports from folder.""" 32 | path = os.path.join(os.path.dirname(__file__), folder) 33 | for f in os.listdir(path): 34 | fpath = os.path.join(path, f) 35 | if f.startswith(".") or not os.path.isfile(fpath): 36 | continue 37 | self.import_report(fpath) 38 | 39 | def import_fail_reports(self, folder="fail-reports"): 40 | """Import failed reports from folder.""" 41 | path = os.path.join(os.path.dirname(__file__), folder) 42 | for f in os.listdir(path): 43 | fpath = os.path.join(path, f) 44 | if f.startswith(".") or not os.path.isfile(fpath): 45 | continue 46 | with self.assertRaisesMessage(SystemExit, '65'): 47 | self.import_report(fpath) 48 | -------------------------------------------------------------------------------- /modoboa_dmarc/tests/reports/Report_Domain_ngyn.org_Submitter_163.com_Report-ID_aggr_report_ngyn.org_20150705_163.com.eml: -------------------------------------------------------------------------------- 1 | Return-Path: 2 | Delivered-To: 3 | Received: from mail.koalabs.org 4 | by nelson.ngyn.org (Dovecot) with LMTP id IKPdD1ULmlXgLQAABvoInA 5 | for ; Mon, 06 Jul 2015 07:00:05 +0200 6 | Received: from localhost (localhost.localdomain [127.0.0.1]) 7 | by mail.koalabs.org (Postfix) with ESMTP id 2645EE035B 8 | for ; Mon, 6 Jul 2015 07:00:05 +0200 (CEST) 9 | X-Virus-Scanned: Debian amavisd-new at mail.koalabs.org 10 | Received: from mail.koalabs.org ([127.0.0.1]) 11 | by localhost (nelson.ngyn.org [127.0.0.1]) (amavisd-new, port 10024) 12 | with ESMTP for ; 13 | Mon, 6 Jul 2015 07:00:03 +0200 (CEST) 14 | Received: from m12-181.163.com (m12-181.163.com [220.181.12.181]) 15 | by mail.koalabs.org (Postfix) with ESMTP 16 | for ; Mon, 6 Jul 2015 06:59:54 +0200 (CEST) 17 | Received: from 163.com (unknown [192.168.201.141]) 18 | by mfast1 (Coremail) with SMTP id tcCowEAJixEvC5pVHJvvLg--.53754S2; 19 | Mon, 06 Jul 2015 12:59:27 +0800 (CST) 20 | Content-Type: multipart/mixed; boundary="===============2544031943362770105==" 21 | MIME-Version: 1.0 22 | From: 23 | Date: Mon, 06 Jul 2015 12:59:25 +0800 24 | Subject: Report Domain: ngyn.org Submitter: 163.com Report-ID: 25 | aggr_report_ngyn.org_20150705_163.com 26 | To: postmaster@ngyn.org 27 | X-CM-TRANSID:tcCowEAJixEvC5pVHJvvLg--.53754S2 28 | Message-Id:<559A0B30.EBEE1F.29746@m12-181.163.com> 29 | X-Coremail-Antispam: 1UD129KBjDUn29KB7ZKAUJUUUUU529EdanIXcx71UUUUU7v73 30 | VFW2AGmfu7bjvjm3AaLaJ3UjIYCTnIWjp_UUUnI7CY07I20VC2zVCF04k26cxKx2IYs7xG 31 | 6rWj6s0DM28lY4IEw2IIxxk0rwA2F7IY1VAKz4vEj48ve4kI8wAa7VASzI0EjI02j7AqF2 32 | xKxVCjxxvEa2IrM2AIxVAIcxkEcVAq07x20xvEncxIr21le4C267I2x7xF54xIwI1l5I8C 33 | rVACY4xI64kE6c02F40Ex7xfMcIj6x8ErcxFaVAv8VWrMcvjeVCFs4IE7xkEbVWUJVW8Jw 34 | ACjcxG0xvEwIxGrwAKzVAC0xCFj2AI6cx7M4kE6xkIj40Ew7xC0wCjxxvEa2IrMxkIecxE 35 | wVCm-wCY0x0Ix7I2Y4AK64vIr41l42xK82IYc2Ij64vIr41l42xK82IY6x8ErcxFaVAv8V 36 | WrMxCjnVAqn7xvrwCFx2IqxVCFs4IE7xkEbVWUJVW8JwC20s026c02F40E14v26r1j6r18 37 | MI8I3I0E7480Y4vE14v26r106r1rMI8E67AF67kF1VAFwI0_Jr0_JrylIxkGc2Ij64vIr4 38 | 1l6VACY4xI67k04243AbIYCTnIWIevJa73UjIFyTuYvjTRClkVDUUUU 39 | X-Originating-IP: [192.168.201.141] 40 | X-CM-SenderInfo: 5dex2vi6rwjhhfrp/ 41 | 42 | --===============2544031943362770105== 43 | Content-Type: text/plain; charset="us-ascii" 44 | MIME-Version: 1.0 45 | Content-Transfer-Encoding: 7bit 46 | 47 | This is a DMARC aggregate report for domain ngyn.org on 20150705. For more information please mail to abuse@163.com. 48 | --===============2544031943362770105== 49 | MIME-Version: 1.0 50 | Content-Type: application/zip; 51 | name="163.com!ngyn.org!1436054400!1436140799.zip" 52 | Content-Transfer-Encoding: base64 53 | Content-Disposition: attachment; 54 | filename="163.com!ngyn.org!1436054400!1436140799.zip" 55 | 56 | UEsDBBQAAAAIAGxn5kZGEGLBdAEAAJQDAAAqAAAAMTYzLmNvbSFuZ3luLm9yZyExNDM2MDU0NDAw 57 | ITE0MzYxNDA3OTkueG1sbVNbbsMgEPzvKXICg5M4USSEehOEzdpBsQEBbpXbF4eHadovzOzs7Owu 58 | JiOA6PnwoB+HA7FgtPVsAc8F93zDAqrtxBRfgLaXUzPohaCCRAIsXM6U96uDz8KJYCQkXSkonybL 59 | 0lVNT9VsUkfcdviKO1aS94QoENwAs1xNqWSAepikou35dMHd+YwxQRHJcVDiFW3P+Hq7BT8qi6Hf 60 | aqVa3TYxepbDk5m1n6W7QzGiQ1uKZutBKwIxysVDLtQRFD8S6Mz4wrYzQoYqrYAgk+4uAy4jZvC0 61 | C4RwvCz+ZyeMddA2O7P6u/Tu9GoHYNLQtu2aIz42l3CcguAeydxBr8rTlqD4keFUEL74vIZxiRzY 62 | ZiCd0U56qVWyXSMVb5vBGF5BIJRxpH7HFCgzqZp8qxn2k1sjUoDycpRgXUm7Axdg2Wj1Qtdm30yN 63 | J6E/6YSv/h7eo1tnvytWpvaV19L10tMD3xTSNNKltLXvHb2X28hxhwSVP/EHUEsBAhQDFAAAAAgA 64 | bGfmRkYQYsF0AQAAlAMAACoAAAAAAAAAAAAAAKSBAAAAADE2My5jb20hbmd5bi5vcmchMTQzNjA1 65 | NDQwMCExNDM2MTQwNzk5LnhtbFBLBQYAAAAAAQABAFgAAAC8AQAAAAA= 66 | --===============2544031943362770105==-- 67 | 68 | -------------------------------------------------------------------------------- /modoboa_dmarc/tests/reports/Report_Domain_ngyn.org_Submitter_amazon.com_Report-ID_aggr_report_ngyn.org_20210607-ignored.eml: -------------------------------------------------------------------------------- 1 | Return-Path: postmaster@amazonses.com 2 | Delivered-To: 3 | Received: from mail.koalabs.org 4 | by nelson.ngyn.org (Dovecot) with LMTP id dV+vEDEApVVCdQAABvoInA 5 | for ; Tue, 03 Jun 2021 14:27:34 +0200 6 | Received: from localhost (localhost.localdomain [127.0.0.1]) 7 | by mail.koalabs.org (Postfix) with ESMTP id C28EFE035E 8 | for ; Tue, 03 Jun 2021 14:27:34 +0200 (CEST) 9 | X-Virus-Scanned: Debian amavisd-new at mail.koalabs.org 10 | Received: from mail.koalabs.org ([127.0.0.1]) 11 | by localhost (nelson.ngyn.org [127.0.0.1]) (amavisd-new, port 10024) 12 | with ESMTP for ; 13 | Tue, 03 Jun 2021 14:27:32 +0200 (CEST) 14 | Received: from a7-16.smtp-out.eu-west-1.amazonses.com (a7-16.smtp-out.eu-west-1.amazonses.com [54.240.7.16]) 15 | by mail.koalabs.org (Postfix) with ESMTPS 16 | for ; Mon, 7 Jun 2021 12:28:31 +0200 (CEST) 17 | (envelope-from 01020179e5ffe776-2049ffa3-f9ed-4b84-8bed-26aec834960c-000000@eu-west-1.amazonses.com) 18 | Date: Mon, 7 Jun 2021 10:23:43 +0000 19 | From: postmaster@amazonses.com 20 | To: dmarc-aggrep@ngyn.org 21 | Message-ID: <01020179e5ffe776-2049ffa3-f9ed-4b84-8bed-26aec834960c-000000@eu-west-1.amazonses.com> 22 | Subject: Dmarc Aggregate Report Domain: {ngyn.org} Submitter: {Amazon 23 | SES} Date: {2021-06-06} Report-ID: {12007123-9847-4f23-a7d7-c6837cce89de} 24 | Content-Type: multipart/mixed; 25 | boundary="----=_Part_8428_1073105148.1623061423987" 26 | MIME-Version: 1.0 27 | 28 | ------=_Part_8428_1073105148.1623061423987 29 | Content-Type: text/plain; charset=us-ascii 30 | Content-Transfer-Encoding: 7bit 31 | 32 | This MIME email was sent through Amazon SES. 33 | ------=_Part_8428_1073105148.1623061423987 34 | Content-Type: application/octet-stream; 35 | name=amazonses.com!ngyn.org!1622937600!1623024000.xml.gz 36 | Content-Transfer-Encoding: base64 37 | Content-Disposition: attachment; 38 | filename="amazonses.com!ngyn.org!1622937600!1623024000.xml.gz" 39 | 40 | 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 277 | yanL1kmbf/kfHM/x+WY1AAA= 278 | 279 | ------=_Part_8428_1073105148.1623061423987-- 280 | -------------------------------------------------------------------------------- /modoboa_dmarc/tests/reports/Report_Domain_ngyn.org_Submitter_amazon.com_Report-ID_aggr_report_ngyn.org_20210622.eml: -------------------------------------------------------------------------------- 1 | Return-Path: postmaster@amazonses.com 2 | Delivered-To: 3 | Received: from mail.koalabs.org 4 | by nelson.ngyn.org (Dovecot) with LMTP id dV+vEDEApVVCdQAABvoInA 5 | for ; Tue, 03 Jun 2021 14:27:34 +0200 6 | Received: from localhost (localhost.localdomain [127.0.0.1]) 7 | by mail.koalabs.org (Postfix) with ESMTP id C28EFE035E 8 | for ; Tue, 03 Jun 2021 14:27:34 +0200 (CEST) 9 | X-Virus-Scanned: Debian amavisd-new at mail.koalabs.org 10 | Received: from mail.koalabs.org ([127.0.0.1]) 11 | by localhost (nelson.ngyn.org [127.0.0.1]) (amavisd-new, port 10024) 12 | with ESMTP for ; 13 | Tue, 03 Jun 2021 14:27:32 +0200 (CEST) 14 | Received: from a7-16.smtp-out.eu-west-1.amazonses.com (a7-16.smtp-out.eu-west-1.amazonses.com [54.240.7.16]) 15 | by mail.koalabs.org (Postfix) with ESMTPS 16 | for ; Mon, 7 Jun 2021 12:28:31 +0200 (CEST) 17 | (envelope-from 01020179e5ffe776-2049ffa3-f9ed-4b84-8bed-26aec834960c-000000@eu-west-1.amazonses.com) 18 | Date: Mon, 7 Jun 2021 10:23:43 +0000 19 | From: postmaster@amazonses.com 20 | To: dmarc-aggrep@ngyn.org 21 | Message-ID: <01020179e5ffe776-2049ffa3-f9ed-4b84-8bed-26aec834960c-000000@eu-west-1.amazonses.com> 22 | Subject: Dmarc Aggregate Report Domain: {ngyn.org} Submitter: {Amazon 23 | SES} Date: {2021-06-06} Report-ID: {12007123-9847-4f23-a7d7-c6837cce89de} 24 | Content-Type: multipart/mixed; 25 | boundary="----=_Part_8428_1073105148.1623061423987" 26 | MIME-Version: 1.0 27 | 28 | ------=_Part_8428_1073105148.1623061423987 29 | Content-Type: text/plain; charset=us-ascii 30 | Content-Transfer-Encoding: 7bit 31 | 32 | This MIME email was sent through Amazon SES. 33 | ------=_Part_8428_1073105148.1623061423987 34 | Content-Type: application/octet-stream; 35 | name=amazonses.com!ngyn.org!1622937600!1623024000.xml.gz 36 | Content-Transfer-Encoding: base64 37 | Content-Disposition: attachment; 38 | filename="amazonses.com!ngyn.org!1622937600!1623024000.xml.gz" 39 | 40 | H4sICA343WAAA2FtYXpvbnNlcy5jb20hbmd5bi5vcmchMTYyNDIzMzYwMCExNjI0MzIwMDAwLnht 41 | bAClU7ty2zAQrKWv4LgX3xalGQSOi5RJCndpOBBwlDAmAQ4AOnG+PgeCryhJlYp3u4e9WxxInn50 42 | bfQGxkqtPjxkcfrwRPekARAXxl/pfkcmkqZxRpI5QdxAr42rO3BMMMcQ2hFtrrViHdDnz8/fvn45 43 | vHx6IckC+gromGxpr63rmHVgPrKO/dTKgo257kgSeF856UtBszxNqywvDudTWR3KBiNWierAj6ei 44 | 4hxOZwEkWev9aRwJasPUdWy7Ixe4SkWzY17mRXFMU5IEZCRBiZEqsJGnfO5Fkt9Ulhar5SgivW4l 45 | f6/74dJKewNBd4giLjQ6UVRd31WMN4BaAZhoJl5lRy1JQjCjtm9G0H8nrKdKK/TXLwB39BFz/HgE 46 | o/sRPGqAazOGPtHfQ4Sx1YPhUMuenss4L+LyMc5OuNoVnyu5HpSjSIVghqd28MbaAe9HzIQ3LS2u 47 | Vjr/RMLUW2RT5z33zKLVMVwZbzwQPppbJn/viRuZjREpQDnZSHygy7EbMAGmbozuNovYopPMH4cJ 48 | G9ytNmCH1q16d7P+a8VrRRCYDE3J4mmrRjZu/195EcOXdOfDF48PI4r2JFn/819WLGN5CQQAAA== 49 | 50 | ------=_Part_8428_1073105148.1623061423987-- 51 | -------------------------------------------------------------------------------- /modoboa_dmarc/tests/reports/Report_Domain_ngyn.org_Submitter_fastmail.com_Report-ID_2015.06.23.5672770.eml: -------------------------------------------------------------------------------- 1 | Return-Path: 2 | Delivered-To: 3 | Received: from mail.koalabs.org 4 | by nelson.ngyn.org (Dovecot) with LMTP id MN7aCZgMilV1TAAABvoInA 5 | for ; Wed, 24 Jun 2015 03:49:12 +0200 6 | Received: from localhost (localhost.localdomain [127.0.0.1]) 7 | by mail.koalabs.org (Postfix) with ESMTP id 0FF2EE035E 8 | for ; Wed, 24 Jun 2015 03:49:12 +0200 (CEST) 9 | X-Virus-Scanned: Debian amavisd-new at mail.koalabs.org 10 | Authentication-Results: nelson.ngyn.org (amavisd-new); 11 | dkim=pass (1024-bit key) header.d=fastmail.com header.b=0Nj5GpwR; 12 | dkim=pass (1024-bit key) header.d=messagingengine.com 13 | header.b=W7jB990M 14 | Received: from mail.koalabs.org ([127.0.0.1]) 15 | by localhost (nelson.ngyn.org [127.0.0.1]) (amavisd-new, port 10024) 16 | with ESMTP for ; 17 | Wed, 24 Jun 2015 03:49:11 +0200 (CEST) 18 | Received: from bulk2-smtp.messagingengine.com (bulk2-smtp.messagingengine.com [66.111.4.225]) 19 | (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) 20 | (No client certificate requested) 21 | by mail.koalabs.org (Postfix) with ESMTPS 22 | for ; Wed, 24 Jun 2015 03:49:11 +0200 (CEST) 23 | Received: from compute6.internal (compute6.nyi.internal [10.202.2.46]) 24 | by mailbulk.nyi.internal (Postfix) with ESMTP id D8DDDAFE; 25 | Tue, 23 Jun 2015 21:49:03 -0400 (EDT) 26 | Received: from frontend1 ([10.202.2.160]) 27 | by compute6.internal (MEProxy); Tue, 23 Jun 2015 21:49:03 -0400 28 | DKIM-Signature: v=1; a=rsa-sha1; c=relaxed/relaxed; d=fastmail.com; h= 29 | content-type:date:from:message-id:mime-version:subject:to 30 | :x-sasl-enc:x-sasl-enc; s=mesmtp; bh=1t7zGOXcrQ9ovOrxVPikV8Cc8h4 31 | =; b=0Nj5GpwRFukRLOhanfU0ZrV04k7RkzI1k4Ko8nC1WFOPbK43dzMSg6zZ1QM 32 | +Hgb+WdYhPc1D1oInpbxoN5urVBNqOL43xyQY7Ul0fg+5kY8s8Pxndo2pFko2Sqf 33 | YvTXeDN9IZJgxcT3Qy1/N38sE0q+Jt3uD+0gTJhJAOZqVlxY= 34 | DKIM-Signature: v=1; a=rsa-sha1; c=relaxed/relaxed; d= 35 | messagingengine.com; h=content-type:date:from:message-id 36 | :mime-version:subject:to:x-sasl-enc:x-sasl-enc; s=smtpout; bh=1t 37 | 7zGOXcrQ9ovOrxVPikV8Cc8h4=; b=W7jB990M1394jwz9JB5XcuQlJ1qTn478nI 38 | nG4+68z4YRi88Nl0Mwmq3LMkamLzim/HSrTSsN2dk2yWl+JxQZNis0Nh6nqq7f60 39 | mkTmv74DLMnAKdqriTpkzN7PnzKTr/A4AumpPF0yFJ3DONjq1fqXFHOAx+T8xk7I 40 | zf1/lQlPs= 41 | X-Sasl-enc: pVVGFLeGBqO3p41F4hif6WoWvBVprb8iTS5Hykw0gj6x 1435110543 42 | Received: from root1.messagingengine.com (unknown [66.111.4.4]) 43 | by mail.messagingengine.com (Postfix) with ESMTPA id A2C52C00288 44 | for ; Tue, 23 Jun 2015 21:49:03 -0400 (EDT) 45 | From: dmarc@fastmail.com 46 | To: postmaster@ngyn.org 47 | Date: Tue, 23 Jun 2015 21:49:03 -0400 48 | Subject: Report Domain: ngyn.org Submitter: fastmail.com Report-ID: 49 | 2015.06.23.5672770 50 | MIME-Version: 1.0 51 | Content-Type: multipart/mixed; boundary="1435110543.af000f16737.3591227"; charset="us-ascii" 52 | Message-Id: <20150624014903.A2C52C00288@frontend1.nyi.internal> 53 | 54 | 55 | --1435110543.af000f16737.3591227 56 | Date: Tue, 23 Jun 2015 21:49:03 -0400 57 | MIME-Version: 1.0 58 | Content-Type: text/plain; charset="US-ASCII" 59 | Content-Disposition: inline 60 | 61 | 62 | This is a DMARC aggregate report for ngyn.org 63 | 64 | 1 records. 65 | 0 passed. 66 | 1 failed. 67 | 68 | Submitted by FastMail Pty Ltd 69 | Generated with Mail::DMARC 1.20150611 70 | 71 | 72 | --1435110543.af000f16737.3591227 73 | Date: Tue, 23 Jun 2015 21:49:03 -0400 74 | MIME-Version: 1.0 75 | Content-Type: application/gzip; name="fastmail.com!ngyn.org!1435017600!1435103999!5672770.xml.gz" 76 | Content-Transfer-Encoding: base64 77 | Content-Disposition: inline; 78 | filename="fastmail.com!ngyn.org!1435017600!1435103999!5672770.xml.gz" 79 | 80 | H4sIAI8MilUAA21TTXPbIBA9J7/Ck3uFlNTWuENJTjm1M/0HGgwrmYn4GEBu/O+7gCzZbk5i31t2 81 | 31tW9PVTj5sT+KCs+fnUVPXTK3ukPYA8cPHBHh+oB2d97DRELnnkCC2Ykmy7a5/btqZkhVKC9UNn 82 | uAb2zkP8zdW4+RPPm19RUrJQKQ80ckxq7sVbj6kprITVlBQm53xGzzthTeQCG5jesmOMLvwg5PoK 83 | wTv/Z6YCKBs6z82Qez7QAwzKsOb7y7Zu2l2N4guSSTAyU039st/vsaYpjshNlcXv1Vios6MS585N 84 | h1GFI5R70qJAw8xwNhVaxzoFSByXH0qzQEk5ZCi4PiPpmwDHjDVAictRuIShxE5EtkUSP0nUFwrw 85 | rYT1RYu3f4vJYCcvoFOOtU3V7Ktmu6uatsWyC5HzhJ1MZM+UlEPG5h5w4uOEI8mVk08VnA0q4h7N 86 | Eq+ROSe57PG9kJwNZ0/9DM6eVyM3TXDmRT9VEkxUvcK1vTzaCUbroIuWwTdhRzucK8HT663EbWbv 87 | rWajOnjAUcV5527InH8ELsEXYH3DazQru1NE+RSPnYcwjXGWePG2bMRd73UtUrJAFUyn+jiVHBSi 88 | lGSOh5BWMAdlYpd9IXetU1pZAErW3/of7jTwqvgDAAA= 89 | 90 | --1435110543.af000f16737.3591227-- 91 | -------------------------------------------------------------------------------- /modoboa_dmarc/tests/reports/Report_Domain_ngyn.org_Submitter_google.com_Report-ID_1282989064754998675.eml: -------------------------------------------------------------------------------- 1 | Return-Path: 2 | Delivered-To: 3 | Received: from mail.koalabs.org 4 | by nelson.ngyn.org (Dovecot) with LMTP id dV+vEDEApVVCdQAABvoInA 5 | for ; Tue, 03 Jun 2021 14:27:34 +0200 6 | Received: from localhost (localhost.localdomain [127.0.0.1]) 7 | by mail.koalabs.org (Postfix) with ESMTP id C28EFE035E 8 | for ; Tue, 03 Jun 2021 14:27:34 +0200 (CEST) 9 | X-Virus-Scanned: Debian amavisd-new at mail.koalabs.org 10 | Received: from mail.koalabs.org ([127.0.0.1]) 11 | by localhost (nelson.ngyn.org [127.0.0.1]) (amavisd-new, port 10024) 12 | with ESMTP for ; 13 | Tue, 03 Jun 2021 14:27:32 +0200 (CEST) 14 | Received: from mail-vn0-f74.google.com (mail-vn0-f74.google.com [209.85.216.74]) 15 | (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) 16 | (No client certificate requested) 17 | by mail.koalabs.org (Postfix) with ESMTPS 18 | for ; Tue, 03 Jun 2021 14:27:29 +0200 (CEST) 19 | Received: by vnbf7 with SMTP id f7so392460vnb.1 20 | for ; Tue, 03 Jun 2021 05:27:28 -0700 (PDT) 21 | MIME-Version: 1.0 22 | X-Received: by 10.52.251.34 with SMTP id zh2mr50557742vdc.8.1436869893195; 23 | Tue, 03 Jun 2021 03:31:33 -0700 (PDT) 24 | Message-ID: <14872432101891615947@google.com> 25 | Date: Tue, 03 Jun 2021 10:31:33 +0000 26 | Subject: Report domain: ngyn.org Submitter: google.com Report-ID: 1282989064754998675 27 | From: noreply-dmarc-support@google.com 28 | To: dmarc-aggrep@ngyn.org 29 | Content-Type: application/zip; 30 | name="google.com!ngyn.org!1622592000!1622678399.zip" 31 | Content-Disposition: attachment; 32 | filename="google.com!ngyn.org!1622592000!1622678399.zip" 33 | Content-Transfer-Encoding: base64 34 | 35 | UEsDBBQDAAAIAMhzw1IU0YnahAIAAAQMAAAtAAAAZ29vZ2xlLmNvbSFuZ3luLm9yZyExNjIyNTky 36 | MDAwITE2MjI2NzgzOTkueG1s7ZbBctsgFEX3+QqP9wYJ24rUIaSrfkG71hDpWWYiAQM4if++WAJJ 37 | ressmridTrOSuPCu3rs+w5jev3Tt4gmMFUreLVOULBcgK1UL2dwtv339ssqXi3t2Q3cA9QOvHtnN 38 | YkENaGVc2YHjNXf8pHlVmaaUvAPWKNW0gCrVURzFcAY6LlomlXdoj6u646Za2YM+2X2el/XnYs2L 39 | M7yslHS8cqWQO8X2zmn7CeNQiqZSzDGX9hkMJpss2+YJxef1wTiMIWqWkpwUeZFkm9vtpijy7HZL 40 | 8bgdjvtRoTRcNmEYLz1AIyRLM0K2BUkS/7FBifsg6343u83XRUExyGiGf3Sj+BeZUq1aUR1LfXho 41 | hd3D2Ijy6Ugmm6NEPl+KByHs8vpRdMxS3L9E0epdr/lnkDSTSgLFOqxtFGxUdOWYz8E/hhbP2hlY 42 | qJSJnRn1PM5u1cFUUArNSFKgfIsISVDmQ5g24tFKHaRjhOL+JcpxfHji7YG7OP4QgbBaWeE8taHr 43 | uTKd6yPYeZYontII4+7CxhhJgILbYBEld9TAWlXxthxaoriX5mc8eh343j3Qd5pbH3VUJmscveP6 44 | fMBBH2OkovYWYifA2LFsD7wGU+6M6mYMzNRoc1ZM+cHtSwP20LrJbwpmThe88E63sPJjIJKk2yQj 45 | a9Rwra3tnEZejtzNagdrNgQQFrPIoYXKKcOiH8VRmjKJ3cQf6WJnr7YQoAiL0XyiH/8cBcU9yb8D 46 | 9Sa9DPXmWlD3Gb8K9b/BWXR+C00kTdbr9G00SXB/kaa0IChBBKXJKyit/zhLp40Pli6w9F7ff2eQ 47 | crRNPUcJWl9GKb0WSafL5/+4lU4DXY+kcCtdDSaKpz/03wFQSwECPwMUAwAACADIc8NSFNGJ2oQC 48 | AAAEDAAALQAkAAAAAAAAACCAtIEAAAAAZ29vZ2xlLmNvbSFuZ3luLm9yZyExNjIyNTkyMDAwITE2 49 | MjI2NzgzOTkueG1sCgAgAAAAAAABABgAgCVxNHRY1wGAJXE0dFjXAQBf60Z0WNcBUEsFBgAAAAAB 50 | AAEAfwAAAM8CAAAAAA== 51 | 52 | -------------------------------------------------------------------------------- /modoboa_dmarc/tests/reports/Report_Domain_ngyn.org_Submitter_google.com_Report-ID_14872432101891615947.eml: -------------------------------------------------------------------------------- 1 | Return-Path: 2 | Delivered-To: 3 | Received: from mail.koalabs.org 4 | by nelson.ngyn.org (Dovecot) with LMTP id dV+vEDEApVVCdQAABvoInA 5 | for ; Tue, 14 Jul 2015 14:27:34 +0200 6 | Received: from localhost (localhost.localdomain [127.0.0.1]) 7 | by mail.koalabs.org (Postfix) with ESMTP id C28EFE035E 8 | for ; Tue, 14 Jul 2015 14:27:34 +0200 (CEST) 9 | X-Virus-Scanned: Debian amavisd-new at mail.koalabs.org 10 | Authentication-Results: nelson.ngyn.org (amavisd-new); 11 | dkim=pass (2048-bit key) header.d=google.com 12 | Received: from mail.koalabs.org ([127.0.0.1]) 13 | by localhost (nelson.ngyn.org [127.0.0.1]) (amavisd-new, port 10024) 14 | with ESMTP for ; 15 | Tue, 14 Jul 2015 14:27:32 +0200 (CEST) 16 | Received: from mail-vn0-f74.google.com (mail-vn0-f74.google.com [209.85.216.74]) 17 | (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) 18 | (No client certificate requested) 19 | by mail.koalabs.org (Postfix) with ESMTPS 20 | for ; Tue, 14 Jul 2015 14:27:29 +0200 (CEST) 21 | Received: by vnbf7 with SMTP id f7so392460vnb.1 22 | for ; Tue, 14 Jul 2015 05:27:28 -0700 (PDT) 23 | DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; 24 | d=google.com; s=20120113; 25 | h=mime-version:message-id:date:subject:from:to:content-type 26 | :content-disposition:content-transfer-encoding; 27 | bh=28gVVTJdqkSuhoNLWpBiJ0mYAF1CETmzecT8+PvUARY=; 28 | b=jahe/NPogONhyXBkFG2iEkgVoYJmOoXuXCsfTUiY4WtDacSfBxs4pHCYd9kv1iu7Rm 29 | 8TUOyMG0ftm+Bu3Ldctb75lcrEb6yf/X/LIhXUyPlWSOwowqU429D8+89Lz2234K+5Ps 30 | 9u4E4vSI4HvVKjqxs/eIkSSpmqZDZydFtqOiKg3Nj4fr9v2FzEHuiwOvqO6dU8lct3zs 31 | cc9/NNNp9q2z+nJ02hujEKDDrqEW+U3Xm8uk+YE4eUDvvqQQyC5SFTRWcGiQ1visZmfm 32 | OlHmv25II7saHmZ4VrGXFUZHE+8d0lXN+QOndSZ6aorsarKzSRlXvD2ISxKKo3Qw8UqJ 33 | CCnA== 34 | MIME-Version: 1.0 35 | X-Received: by 10.52.251.34 with SMTP id zh2mr50557742vdc.8.1436869893195; 36 | Tue, 14 Jul 2015 03:31:33 -0700 (PDT) 37 | Message-ID: <14872432101891615947@google.com> 38 | Date: Tue, 14 Jul 2015 10:31:33 +0000 39 | Subject: Report domain: ngyn.org Submitter: google.com Report-ID: 14872432101891615947 40 | From: noreply-dmarc-support@google.com 41 | To: dmarc-aggrep@ngyn.org 42 | Content-Type: application/zip; 43 | name="google.com!ngyn.org!1436745600!1436831999.zip" 44 | Content-Disposition: attachment; 45 | filename="google.com!ngyn.org!1436745600!1436831999.zip" 46 | Content-Transfer-Encoding: base64 47 | 48 | UEsDBAoAAAAIANhS7kbvUmeO+AEAANQEAAAtAAAAZ29vZ2xlLmNvbSFuZ3luLm9yZyExNDM2NzQ1 49 | NjAwITE0MzY4MzE5OTkueG1spVTLcqMwELznK1y+Gxkb27ClKDnlC3bPlCwGrApIKkkk8d/vYImH 50 | na1c9oTo6WlNN2PTl6+uXX2AdVKr53WabNcrUEJXUjXP6z+/3zb5evXCnmgNUJ25eGdPqxW1YLT1 51 | ZQeeV9zzAUNU26ZUvAPWaN20kAjdUTKBgQMdly1TGhXa66bquBUb15tB7nXZFnix58tbXgqtPBe+ 52 | lKrW7OK9cb8Iia3J3Eo44cp9giW77Hg85FvU+t4fhKMNWbE0y0+7bL9Lt2lepMf0UGQnSuZ64KNX 53 | KC1XTXSD0BkaqbB9fzxlh+MWbwvIWAdV3ar5Pi2KAmdRoxi5V5tuW4ZKjW6luJamP7fSXWAaRGM8 54 | iqnmqhIMGLUCEKq8epcdc5SEQwSdqW/Y8AyQwe+ggBIT390IuBExwrMDEvBxG/Ff42CKQttxMqs/ 55 | J+9O91ZAKQ0rsmS3T7JDkuYpyk/4yBS6V55hKRxGOF4HH7ztMaxqLAwJSGe0kx63Ng69RBa8IQHD 56 | HTqfw4hu61iYEolLwd1SAiF/NcAc70wLVal7T8kNWVJw8zrA0QcL4TQrknvJKcYHX8gbw6OyQglZ 57 | S/xdTm0X4BXYsra6W3z5JRplvjVT3vtLacH1rZ/1HvL4cadiMoNADC2+TJ6WavQ+0f9UnheWPPoY 58 | yGH5KJn/oP4CUEsBAgoACgAAAAgA2FLuRu9SZ474AQAA1AQAAC0AAAAAAAAAAAAAAAAAAAAAAGdv 59 | b2dsZS5jb20hbmd5bi5vcmchMTQzNjc0NTYwMCExNDM2ODMxOTk5LnhtbFBLBQYAAAAAAQABAFsA 60 | AABDAgAAAAA= 61 | -------------------------------------------------------------------------------- /modoboa_dmarc/tests/reports/Report_Domain_ngyn.org_Submitter_hotmail.com_Report-ID_66f317a021ff4cdabad2b350d3303615@hotmail.com.eml: -------------------------------------------------------------------------------- 1 | Return-Path: 2 | Delivered-To: 3 | Received: from mail.koalabs.org 4 | by nelson.ngyn.org (Dovecot) with LMTP id mL/aJNE4kFUCCAAABvoInA 5 | for ; Sun, 28 Jun 2015 20:11:29 +0200 6 | Received: from localhost (localhost.localdomain [127.0.0.1]) 7 | by mail.koalabs.org (Postfix) with ESMTP id 7F4A2E035D 8 | for ; Sun, 28 Jun 2015 20:11:29 +0200 (CEST) 9 | X-Virus-Scanned: Debian amavisd-new at mail.koalabs.org 10 | Received: from mail.koalabs.org ([127.0.0.1]) 11 | by localhost (nelson.ngyn.org [127.0.0.1]) (amavisd-new, port 10024) 12 | with ESMTP for ; 13 | Sun, 28 Jun 2015 20:11:28 +0200 (CEST) 14 | Received: from mail2.koalabs.org (alena.koalabs.org [66.175.223.133]) 15 | by mail.koalabs.org (Postfix) with ESMTP 16 | for ; Sun, 28 Jun 2015 20:11:28 +0200 (CEST) 17 | Received: from BAY004-OMC4S15.hotmail.com (bay004-omc4s15.hotmail.com [65.54.190.217]) 18 | by mail2.koalabs.org (Postfix) with ESMTP id 63F8D46F66 19 | for ; Sun, 28 Jun 2015 14:11:26 -0400 (EDT) 20 | Received: from BAY0-DTS-DRS01 ([65.54.190.200]) by BAY004-OMC4S15.hotmail.com with Microsoft SMTPSVC(7.5.7601.23008); 21 | Sun, 28 Jun 2015 11:11:18 -0700 22 | MIME-Version: 1.0 23 | From: dmarcrep@microsoft.com 24 | To: postmaster@ngyn.org 25 | Date: 28 Jun 2015 11:11:18 -0700 26 | Subject: =?utf-8?B?UmVwb3J0IERvbWFpbjogbmd5bi5vcmcgU3VibWl0dGVyOiBob3RtYWlsLmNvbSBSZXBvcnQtSUQ6IDw2NmYzMTdhMDIxZmY0Y2RhYmFkMmIzNTBkMzMwMzYxNUBob3RtYWlsLmNvbT4=?= 27 | Content-Type: multipart/mixed; 28 | boundary=--boundary_32281_b52cf564-2a50-4f96-aeb0-ef5f83b05463 29 | Message-ID: 30 | X-OriginalArrivalTime: 28 Jun 2015 18:11:18.0588 (UTC) FILETIME=[D4889FC0:01D0B1CD] 31 | 32 | 33 | ----boundary_32281_b52cf564-2a50-4f96-aeb0-ef5f83b05463 34 | Content-Type: text/plain; charset=us-ascii 35 | Content-Transfer-Encoding: quoted-printable 36 | 37 | 38 | ----boundary_32281_b52cf564-2a50-4f96-aeb0-ef5f83b05463 39 | Content-Type: application/zip; name=hotmail.com!ngyn.org!1435428000!1435514400.zip 40 | Content-Transfer-Encoding: base64 41 | Content-Disposition: attachment; filename=hotmail.com!ngyn.org!1435428000!1435514400.zip 42 | 43 | UEsDBBQAAgAIAGlZ3EbQIUrLnQEAADsDAAAuAF0AaG90bWFpbC5jb20hbmd5bi5vcmchMTQz 44 | NTQyODAwMCExNDM1NTE0NDAwLnhtbFNESACMAAAAAAgAiyIcyGNkYGkRYWBgUGCAAB0gZmQE 45 | M1lF0NhMDAkMLCCegATDf0Z5BkYmiBxIryITdnEFsLgIRBxqlhADphjIfABVVA0AB8Y4kFXG 46 | OJBVxjiQVYWT226cMBCGX2W198WwwHYrTZxIue4zIGOPWSv4INvk8Gy96CP1FWrjsCGKqt7g 47 | 8W/+mW/G8OfXb7h/1fPhGX1Q1twdm6o+HtBwK5SZ7o5LlN8ux3sKElGMjD9R8Oisj4PGyASL 48 | jIL102CYRvpTcW+DlfHwaL2rgNxOADVTMxWaeZ78D3p7s+JWAymnW2Yl6Pks2+Y7q0+NlB0X 49 | bGTiNLZ9Ldq2bs9N/3C1MXuK/cMHiQgHz8yUao44KUObru2706WuayBFATRilfum67Kc90D2 50 | VvKlSWdnxd8Gt4yzClfMpWwiMNRMb6ZKjaYERQAmnpSmAUgJgAUn121ewVFjDQJxFMIWpwAc 51 | j7RPclrS80s1j9z6vNqXZLSL5zgoR3901amtur5qLk3Kc9OB28VEmrQSbPz4zOYlNZr5VXA2 52 | qJiu/R1jr8DK7lhI4KWNTC/TzDOu/GDcZSQrnBJoopIqfVEUrsgE+kF6q3eT2qtAPhnYEq+D 53 | x7DMMZSa/x50eY1G1A69tz5f26q8Ixbu/9lLjzdnMZHPHGQbP7n9B38BUEsBAhcLFAACAAgA 54 | aVncRtAhSsudAQAAOwMAAC4AEQAAAAAAAQAgALaBAAAAAGhvdG1haWwuY29tIW5neW4ub3Jn 55 | ITE0MzU0MjgwMDAhMTQzNTUxNDQwMC54bWxTRAQAjAAAAFVUBQAHxjiQVVBLBQYAAAAAAQAB 56 | AG0AAABGAgAAAAA= 57 | ----boundary_32281_b52cf564-2a50-4f96-aeb0-ef5f83b05463-- 58 | 59 | 60 | -------------------------------------------------------------------------------- /modoboa_dmarc/tests/reports/Report_Domain_ngyn.org_Submitter_yahoo.com_Report-ID_1435111091.916236.eml: -------------------------------------------------------------------------------- 1 | Return-Path: 2 | Delivered-To: 3 | Received: from mail.koalabs.org 4 | by nelson.ngyn.org (Dovecot) with LMTP id 53eANBaoilWVQwAABvoInA 5 | for ; Wed, 24 Jun 2015 14:52:38 +0200 6 | Received: from localhost (localhost.localdomain [127.0.0.1]) 7 | by mail.koalabs.org (Postfix) with ESMTP id BD629E0360 8 | for ; Wed, 24 Jun 2015 14:52:38 +0200 (CEST) 9 | X-Virus-Scanned: Debian amavisd-new at mail.koalabs.org 10 | Authentication-Results: nelson.ngyn.org (amavisd-new); 11 | dkim=pass (2048-bit key) header.d=yahoo.com 12 | Received: from mail.koalabs.org ([127.0.0.1]) 13 | by localhost (nelson.ngyn.org [127.0.0.1]) (amavisd-new, port 10024) 14 | with ESMTP for ; 15 | Wed, 24 Jun 2015 14:52:37 +0200 (CEST) 16 | Received: from n1-vm5.bullet.mail.ne1.yahoo.com (n1-vm5.bullet.mail.ne1.yahoo.com [98.138.229.197]) 17 | (using TLSv1 with cipher ECDHE-RSA-RC4-SHA (128/128 bits)) 18 | (No client certificate requested) 19 | by mail.koalabs.org (Postfix) with ESMTPS 20 | for ; Wed, 24 Jun 2015 14:52:37 +0200 (CEST) 21 | DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yahoo.com; s=s2048; t=1435150193; bh=s8fRltU+y/nfIjdSVEbtwFXKmxeW2BeOfADZ7qSc3i4=; h=Date:From:To:Subject:From:Subject; b=Masqn66+cIcv0cwUblZZjLPFOA+l0BfXvanLooG2xAWCwGItLHUToHXwfhsbw5QsyhpptvQSIFWFUBoZqS8T27ZcLVzkPRFl38h/Yo03pBDwL6njc1yQVN2RZxz3i7VO5MXEF2XFg9z2B19dfi+GRISCGLTPA0s+sAt32I+A8FZDSzqDxb3AKuU66QabM9BJiMn/XUGAr7At96sEFyWPDBWe2Q3+MzEN5pO54uWVhjd9qaIRB6Oc/FAFM+SFcVU9UrJFRN43rP/50xV70rrV00//lWrJH+4b3RdIHjy54Vy0qLwOJ8PRdKaDbJ/NJuxbKNiYJZvE+t+QvlEpbvd7/Q== 22 | Received: from [98.138.226.173] by n1.bullet.mail.ne1.yahoo.com with NNFMP; 24 Jun 2015 12:49:53 -0000 23 | Received: from [98.138.237.130] by t2.bullet.mail.ne1.yahoo.com with NNFMP; 24 Jun 2015 12:49:53 -0000 24 | Received: from [127.0.0.1] by launcher100.asd.mail.corp.ne1.yahoo.com with NNFMP; 24 Jun 2015 12:49:53 -0000 25 | X-Yahoo-Newman-Property: asdreport 26 | X-Yahoo-Newman-Id: 1435111091.916236 27 | MIME-Version: 1.0 28 | Content-Transfer-Encoding: binary 29 | Content-Type: multipart/mixed; boundary="_----------=_14351501937631101" 30 | X-Mailer: MIME::Lite 3.027 (F2.73; A2.04; B3.13; Q3.13) 31 | Date: Wed, 24 Jun 2015 05:49:53 -0700 32 | From: noreply@dmarc.yahoo.com 33 | To: postmaster@ngyn.org 34 | Subject: Report Domain: ngyn.org Submitter: yahoo.com Report-ID: <1435111091.916236> 35 | Message-Id: <1435150193.404586@dmarc.yahoo.com> 36 | 37 | This is a multi-part message in MIME format. 38 | 39 | --_----------=_14351501937631101 40 | Content-Disposition: inline 41 | Content-Length: 44 42 | Content-Transfer-Encoding: binary 43 | Content-Type: text/plain 44 | 45 | This is an aggregate report from Yahoo! Inc. 46 | --_----------=_14351501937631101 47 | Content-Disposition: attachment; filename="yahoo.com!ngyn.org!1435017600!1435103999.zip" 48 | Content-Transfer-Encoding: base64 49 | Content-Type: application/x-zip-compressed; name="yahoo.com!ngyn.org!1435017600!1435103999.zip" 50 | 51 | UEsDBBQAAAAIADsu2EY7OXCQrwEAACUEAAAsABwAeWFob28uY29tIW5neW4u 52 | b3JnITE0MzUwMTc2MDAhMTQzNTEwMzk5OS54bWxVVAkAA3GnilVxp4pVdXgL 53 | AAEEAAAAAAQAAAAAlVRBcuMgEDzvvkKbB4CIY7tURche84Q9qTAa2VQkoAAl 54 | 8e8zMhjJTi57EvQ0M90zU+Ivn+NQvYMP2prnB0bqhxfx6zfvAbqDVG94riru 55 | wVkf2xGi7GSUFxBh64+tkSOIf/Jk7Z/q1SjCaUEzC0apB+FsiKMMEfzfbpRe 56 | kfP8hig7cpoYmZ5r6U6wp82WMVY3jDRs97jZcboEMxvlQOulOV7LIXaAozaX 57 | 1zXb7+qa04QUApicvN40TVOhAFMS0ruMpeaNee7soNW5ddNh0OEEix6LXoww 58 | x7Mh2AhMl4Aclt2bHkXgNB2uaHD9BZy/GXPCWAOcugKoKLZ4x0/S9aMEbJ+y 59 | vsjx9mOxHezkFbTaiT0jrCFsuyNsv+d0CRSuspOJouE0HQqei8K7HCZsVFci 60 | s3cdcMo64iZl8WtkTZy99zhzZCxtSBqxAymy9GJl9r4uTqc45LoDE3WvcZmX 61 | lyeQHfi293ZcTWWNXjN9f87lFE+thzANcZXyXnMe8aAPHnAUMS31zeDzZs+J 62 | hIEpejnMi3W5LyZvEvN1A/6/ipMh/FBitWH0m7uZn7eH09Uf4AtQSwECHgMU 63 | AAAACAA7LthGOzlwkK8BAAAlBAAALAAYAAAAAAABAAAApIEAAAAAeWFob28u 64 | Y29tIW5neW4ub3JnITE0MzUwMTc2MDAhMTQzNTEwMzk5OS54bWxVVAUAA3Gn 65 | ilV1eAsAAQQAAAAABAAAAABQSwUGAAAAAAEAAQByAAAAFQIAAAAA 66 | 67 | --_----------=_14351501937631101-- 68 | -------------------------------------------------------------------------------- /modoboa_dmarc/tests/reports/Report_Domain_ngyn.org_Submitter_yahoo.com_Report-ID_1436111091.916236-ignored.eml: -------------------------------------------------------------------------------- 1 | Return-Path: 2 | Delivered-To: 3 | Received: from mail.koalabs.org 4 | by nelson.ngyn.org (Dovecot) with LMTP id 53eANBaoilWVQwAABvoInA 5 | for ; Wed, 24 Jun 2015 14:52:38 +0200 6 | Received: from localhost (localhost.localdomain [127.0.0.1]) 7 | by mail.koalabs.org (Postfix) with ESMTP id BD629E0360 8 | for ; Wed, 24 Jun 2015 14:52:38 +0200 (CEST) 9 | X-Virus-Scanned: Debian amavisd-new at mail.koalabs.org 10 | Authentication-Results: nelson.ngyn.org (amavisd-new); 11 | dkim=pass (2048-bit key) header.d=yahoo.com 12 | Received: from mail.koalabs.org ([127.0.0.1]) 13 | by localhost (nelson.ngyn.org [127.0.0.1]) (amavisd-new, port 10024) 14 | with ESMTP for ; 15 | Wed, 24 Jun 2015 14:52:37 +0200 (CEST) 16 | Received: from n1-vm5.bullet.mail.ne1.yahoo.com (n1-vm5.bullet.mail.ne1.yahoo.com [98.138.229.197]) 17 | (using TLSv1 with cipher ECDHE-RSA-RC4-SHA (128/128 bits)) 18 | (No client certificate requested) 19 | by mail.koalabs.org (Postfix) with ESMTPS 20 | for ; Wed, 24 Jun 2015 14:52:37 +0200 (CEST) 21 | DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yahoo.com; s=s2048; t=1435150193; bh=s8fRltU+y/nfIjdSVEbtwFXKmxeW2BeOfADZ7qSc3i4=; h=Date:From:To:Subject:From:Subject; b=Masqn66+cIcv0cwUblZZjLPFOA+l0BfXvanLooG2xAWCwGItLHUToHXwfhsbw5QsyhpptvQSIFWFUBoZqS8T27ZcLVzkPRFl38h/Yo03pBDwL6njc1yQVN2RZxz3i7VO5MXEF2XFg9z2B19dfi+GRISCGLTPA0s+sAt32I+A8FZDSzqDxb3AKuU66QabM9BJiMn/XUGAr7At96sEFyWPDBWe2Q3+MzEN5pO54uWVhjd9qaIRB6Oc/FAFM+SFcVU9UrJFRN43rP/50xV70rrV00//lWrJH+4b3RdIHjy54Vy0qLwOJ8PRdKaDbJ/NJuxbKNiYJZvE+t+QvlEpbvd7/Q== 22 | Received: from [98.138.226.173] by n1.bullet.mail.ne1.yahoo.com with NNFMP; 24 Jun 2015 12:49:53 -0000 23 | Received: from [98.138.237.130] by t2.bullet.mail.ne1.yahoo.com with NNFMP; 24 Jun 2015 12:49:53 -0000 24 | Received: from [127.0.0.1] by launcher100.asd.mail.corp.ne1.yahoo.com with NNFMP; 24 Jun 2015 12:49:53 -0000 25 | X-Yahoo-Newman-Property: asdreport 26 | X-Yahoo-Newman-Id: 1435111091.916236 27 | MIME-Version: 1.0 28 | Content-Transfer-Encoding: binary 29 | Content-Type: multipart/mixed; boundary="_----------=_14351501937631101" 30 | X-Mailer: MIME::Lite 3.027 (F2.73; A2.04; B3.13; Q3.13) 31 | Date: Wed, 24 Jun 2015 05:49:53 -0700 32 | From: noreply@dmarc.yahoo.com 33 | To: postmaster@ngyn.org 34 | Subject: Report Domain: ngyn.org Submitter: yahoo.com Report-ID: <1436111091.916236> 35 | Message-Id: <1435150193.404586@dmarc.yahoo.com> 36 | 37 | This is a multi-part message in MIME format. 38 | 39 | --_----------=_14351501937631101 40 | Content-Disposition: inline 41 | Content-Length: 44 42 | Content-Transfer-Encoding: binary 43 | Content-Type: text/plain 44 | 45 | This is an aggregate report from Yahoo! Inc. 46 | --_----------=_14351501937631101 47 | Content-Disposition: attachment; filename="yahoo.com!ngyn.org!1436017600!1436103999.zip" 48 | Content-Transfer-Encoding: base64 49 | Content-Type: application/x-zip-compressed; name="yahoo.com!ngyn.org!1436017600!1436103999.zip" 50 | 51 | UEsDBBQDAAAIAHhdTFUTlAO/pwEAABkEAAAsAAAAeWFob28uY29tIW5neW4ub3JnITE0MzYwMTc2 52 | MDAhMTQzNjEwMzk5OS54bWylU0uu2zAMXLenSN8BLKtpExhg9brtEboyFJt21Gd9QMmvze2rSIZk 53 | pNl15fFwOEMSNrz+0cvhHckra7698KZ9eRUfPsKEOF7k8Bbx4QCEzlLoNQY5yiATGWlLc2+kRvFT 54 | Xq39dPhhhgZYYTcVaqkW4awPWvqA9H3Ukobmdu9pBquBJUWS1yw1Cv7leOKctx1vOn76fDwBq8VN 55 | HcfBnqSZc1ziLjgrk7pbfj61LbDEVAGazbw9dl0X803xYw+GwJ7tDs4uarj1br0syl+xtMvxTWnh 56 | gWWwkd5NgoClZ6acIPyFQwDmNsZXKsJNNQTB7/PfQRrmaS4QDpbKDGR/11W9XWnAXjlx5g3vGv71 57 | 1PDzGVgpVO1gVxNEvEcCld9C8V0uqwxl2Xx/5Z31KihrhLEGge2YvTDdY5JqAZbgrhTPslUiqrHs 58 | SW4u1A1BjWiCmhSSr51XlCNSP5HVwsw301iage3Y4vRvO8g1XHtCvy5hZ/k482i1VEYs6kK4KB/y 59 | h5zZvS4bCYNrIBk3zO9FUY5RT/EfKU56/yQim2b8uF2kytcDbPfX/wVQSwECPwMUAwAACAB4XUxV 60 | E5QDv6cBAAAZBAAALAAkAAAAAAAAACCApIEAAAAAeWFob28uY29tIW5neW4ub3JnITE0MzYwMTc2 61 | MDAhMTQzNjEwMzk5OS54bWwKACAAAAAAAAEAGACASwUgH97YAYBLBSAf3tgBAFhOMR/e2AFQSwUG 62 | AAAAAAEAAQB+AAAA8QEAAAAA 63 | 64 | --_----------=_14351501937631101-- 65 | -------------------------------------------------------------------------------- /modoboa_dmarc/tests/test_management_command.py: -------------------------------------------------------------------------------- 1 | """Management command tests.""" 2 | 3 | from modoboa.admin import factories as admin_factories 4 | from modoboa.lib.tests import ModoTestCase 5 | 6 | from . import mixins 7 | from .. import models 8 | 9 | 10 | class ManagementCommandTestCase(mixins.CallCommandMixin, ModoTestCase): 11 | """Test management command.""" 12 | 13 | @classmethod 14 | def setUpTestData(cls): 15 | super(ManagementCommandTestCase, cls).setUpTestData() 16 | cls.domain = admin_factories.DomainFactory(name="ngyn.org") 17 | 18 | def test_import_from_archive(self): 19 | """Import report from archive.""" 20 | self.import_reports() 21 | self.import_fail_reports() 22 | self.assertTrue(self.domain.record_set.exists()) 23 | self.assertTrue( 24 | models.Reporter.objects.filter( 25 | org_name="FastMail Pty Ltd").exists()) 26 | # Ensure that reports from Yahoo are processed successfully. 27 | # These do not contain a sp attribute in policy_published 28 | self.assertTrue( 29 | models.Report.objects.filter( 30 | report_id="1435111091.916236", 31 | reporter=models.Reporter.objects.get(org_name="Yahoo! Inc."), 32 | ).exists() 33 | ) 34 | -------------------------------------------------------------------------------- /modoboa_dmarc/tests/test_views.py: -------------------------------------------------------------------------------- 1 | """Views tests.""" 2 | 3 | from django.urls import reverse 4 | 5 | from modoboa.admin import factories as admin_factories 6 | from modoboa.core import models as core_models 7 | from modoboa.lib.tests import ModoTestCase 8 | 9 | from . import mixins 10 | 11 | 12 | class DMARCViewsTestCase(mixins.CallCommandMixin, ModoTestCase): 13 | """Views test cases.""" 14 | 15 | @classmethod 16 | def setUpTestData(cls): 17 | """Create test data.""" 18 | super(DMARCViewsTestCase, cls).setUpTestData() 19 | cls.domain = admin_factories.DomainFactory(name="ngyn.org") 20 | 21 | def test_domainlist_view(self): 22 | """Test domain list view.""" 23 | self.import_reports() 24 | user = core_models.User.objects.get(username="admin") 25 | self.client.force_login(user) 26 | url = reverse("admin:_domain_list") 27 | response = self.ajax_get(url) 28 | url = reverse("modoboa_dmarc:domain_report", args=[self.domain.pk]) 29 | self.assertIn(url, response["rows"]) 30 | 31 | def test_domainreport_view(self): 32 | """Test domain report view.""" 33 | self.import_reports() 34 | user = core_models.User.objects.get(username="admin") 35 | self.client.force_login(user) 36 | url = reverse("modoboa_dmarc:domain_report", args=[self.domain.pk]) 37 | response = self.client.get("{}?period=2015-26".format(url)) 38 | self.assertContains(response, "'Failed', 100.0") 39 | 40 | def test_domainreport_view_week0(self): 41 | """Test domain report view for week 0.""" 42 | self.import_reports() 43 | user = core_models.User.objects.get(username="admin") 44 | self.client.force_login(user) 45 | url = reverse("modoboa_dmarc:domain_report", args=[self.domain.pk]) 46 | response = self.client.get("{}?period=2019-0".format(url)) 47 | self.assertContains(response, "Dec. 31, 2018") 48 | 49 | def test_domainreport_view_week1(self): 50 | """Test domain report view for week 1.""" 51 | self.import_reports() 52 | user = core_models.User.objects.get(username="admin") 53 | self.client.force_login(user) 54 | url = reverse("modoboa_dmarc:domain_report", args=[self.domain.pk]) 55 | response = self.client.get("{}?period=2019-1".format(url)) 56 | self.assertContains(response, "Jan. 7, 2019") 57 | 58 | def test_domainreport_view_week52(self): 59 | """Test domain report view for week 52.""" 60 | self.import_reports() 61 | user = core_models.User.objects.get(username="admin") 62 | self.client.force_login(user) 63 | url = reverse("modoboa_dmarc:domain_report", args=[self.domain.pk]) 64 | response = self.client.get("{}?period=2018-52".format(url)) 65 | self.assertContains(response, "Dec. 30, 2018") 66 | 67 | def test_domainreport_view_arc(self): 68 | """Test domain report view for displaying ARC results""" 69 | self.import_reports() 70 | user = core_models.User.objects.get(username="admin") 71 | self.client.force_login(user) 72 | url = reverse("modoboa_dmarc:domain_report", args=[self.domain.pk]) 73 | response = self.client.get("{}?period=2021-22".format(url)) 74 | self.assertContains(response, "'Fully aligned', 86.0") 75 | self.assertContains(response, "'Partially aligned', 8.0") 76 | self.assertContains(response, "'Forwarded', 4.0") 77 | self.assertContains(response, "'Failed', 2.0") 78 | -------------------------------------------------------------------------------- /modoboa_dmarc/urls.py: -------------------------------------------------------------------------------- 1 | """DMARC urls.""" 2 | 3 | from django.urls import path 4 | 5 | from . import views 6 | 7 | app_name = "modoboa_dmarc" 8 | 9 | urlpatterns = [ 10 | path("domains//", views.DomainReportView.as_view(), 11 | name="domain_report"), 12 | ] 13 | -------------------------------------------------------------------------------- /modoboa_dmarc/views.py: -------------------------------------------------------------------------------- 1 | """DMARC views.""" 2 | 3 | import concurrent.futures 4 | import datetime 5 | import tldextract 6 | 7 | from dns import resolver, reversename 8 | 9 | from django.db.models import Q 10 | from django.utils import timezone 11 | from django.views import generic 12 | 13 | from django.contrib.auth import mixins as auth_mixins 14 | from django.utils.translation import ugettext_lazy as _ 15 | 16 | from modoboa.admin import models as admin_models 17 | from modoboa.parameters import tools as param_tools 18 | 19 | from . import models 20 | 21 | 22 | def insert_record(target, record, name): 23 | """Add a record.""" 24 | 25 | if name not in target: 26 | target[name] = {} 27 | 28 | if record.source_ip not in target[name]: 29 | target[name][record.source_ip] = { 30 | "total": 0, 31 | "spf": {"pass": 0, "fail": 0}, 32 | "dkim": {"pass": 0, "fail": 0} 33 | } 34 | target[name][record.source_ip]["total"] += record.count 35 | for typ in ["spf", "dkim"]: 36 | result = getattr(record, "{}_result".format(typ)) 37 | target[name][record.source_ip][typ][result] += record.count 38 | 39 | 40 | def week_range(year, weeknumber): 41 | """Return start and end dates of a given week.""" 42 | tz = timezone.get_current_timezone() 43 | fmt = "%Y-%W-%w" 44 | start_week = datetime.datetime.strptime( 45 | "{}-{}-{}".format(year, weeknumber, 1), fmt) 46 | end_week = datetime.datetime.strptime( 47 | "{}-{}-{}".format(year, weeknumber, 0), fmt) 48 | return tz.localize(start_week), tz.localize(end_week) 49 | 50 | 51 | class DomainReportView( 52 | auth_mixins.PermissionRequiredMixin, 53 | generic.TemplateView): 54 | """ListView for Report.""" 55 | 56 | permission_required = "modoboa_dmarc.view_report" 57 | template_name = "modoboa_dmarc/domain_report.html" 58 | 59 | def get_queryset(self): 60 | """Filter reports.""" 61 | self.period = self.request.GET.get("period", "") 62 | if not self.period: 63 | year, week, day = timezone.now().isocalendar() 64 | week -= 1 65 | self.period = "{}-{}".format(year, week) 66 | else: 67 | year, week = self.period.split("-") 68 | 69 | self.daterange = week_range(year, week) 70 | self.domain = admin_models.Domain.objects.get(pk=self.kwargs["pk"]) 71 | qargs = ( 72 | (Q(report__start_date__gte=self.daterange[0], 73 | report__start_date__lte=self.daterange[1]) | 74 | Q(report__end_date__gte=self.daterange[0], 75 | report__end_date__lte=self.daterange[1])) & 76 | Q(header_from=self.domain) 77 | ) 78 | return models.Record.objects.select_related().filter(qargs) 79 | 80 | def get_context_data(self, *args, **kwargs): 81 | """Extra context data.""" 82 | context = super(DomainReportView, self).get_context_data( 83 | *args, **kwargs) 84 | qset = self.get_queryset() 85 | stats = { 86 | "total": 0, 87 | "aligned": 0, 88 | "trusted": 0, 89 | "forwarded": 0, 90 | "failed": 0 91 | } 92 | aligned = {} 93 | trusted = {} 94 | forwarded = {} 95 | threats = {} 96 | all_records = qset.all() 97 | dns_names = {} 98 | if param_tools.get_global_parameter("enable_rlookups"): 99 | dns_resolver = resolver.Resolver() 100 | dns_resolver.timeout = 1.0 101 | dns_resolver.lifetime = 1.0 102 | 103 | def get_domain_name_from_ip(ip): 104 | addr = reversename.from_address(ip) 105 | try: 106 | resp = dns_resolver.query(addr, "PTR") 107 | ext = tldextract.extract(str(resp[0].target)) 108 | if not ext.suffix: # invalid PTR record 109 | raise resolver.NXDOMAIN() 110 | return (ip, '.'.join((ext.domain, ext.suffix)).lower()) 111 | except (resolver.NXDOMAIN, resolver.YXDOMAIN, 112 | resolver.NoAnswer, resolver.NoNameservers, 113 | resolver.Timeout): 114 | return (None, None) 115 | 116 | ips = (r.source_ip for r in all_records) 117 | with concurrent.futures.ThreadPoolExecutor(max_workers=16) as pool: 118 | dns_names = {i: n for (i, n) in 119 | list(pool.map(get_domain_name_from_ip, ips))} 120 | 121 | for record in all_records: 122 | stats["total"] += record.count 123 | name = dns_names.get(record.source_ip, _("Not resolved")) 124 | if record.dkim_result == "pass" and record.spf_result == "pass": 125 | stats["aligned"] += record.count 126 | insert_record(aligned, record, name) 127 | elif record.dkim_result == "pass" or record.spf_result == "pass": 128 | stats["trusted"] += record.count 129 | insert_record(trusted, record, name) 130 | elif record.reason_type == "local_policy" and record.reason_comment.startswith("arc=pass"): 131 | stats["forwarded"] += record.count 132 | insert_record(forwarded, record, name) 133 | else: 134 | insert_record(threats, record, name) 135 | stats["failed"] += record.count 136 | 137 | pie_data = {} 138 | if stats["total"]: 139 | pie_data.update({ 140 | "faligned": stats["aligned"] / float(stats["total"]) * 100, 141 | "paligned": stats["trusted"] / float(stats["total"]) * 100, 142 | "forwarded": stats["forwarded"] / float(stats["total"]) * 100, 143 | "failed": stats["failed"] / float(stats["total"]) * 100 144 | }) 145 | 146 | context.update({ 147 | "stats": stats, "aligned": aligned, "trusted": trusted, "forwarded": forwarded, "threats": threats, 148 | "period": self.period, "daterange": self.daterange, 149 | "domain": self.domain, "pie_data": pie_data 150 | }) 151 | return context 152 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | modoboa>=2.0.0 2 | tldextract>=2.0.2 3 | defusedxml>=0.6.0 4 | python-magic>=0.4.24 5 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [bdist_wheel] 2 | universal = 1 -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | A setuptools based setup module. 5 | 6 | See: 7 | https://packaging.python.org/en/latest/distributing.html 8 | """ 9 | 10 | import io 11 | from os import path 12 | 13 | try: 14 | from pip.req import parse_requirements 15 | except ImportError: 16 | # pip >= 10 17 | from pip._internal.req import parse_requirements 18 | 19 | from setuptools import setup, find_packages 20 | 21 | 22 | def get_requirements(requirements_file): 23 | """Use pip to parse requirements file.""" 24 | requirements = [] 25 | if path.isfile(requirements_file): 26 | for req in parse_requirements(requirements_file, session="hack"): 27 | try: 28 | if req.markers: 29 | requirements.append("%s;%s" % (req.req, req.markers)) 30 | else: 31 | requirements.append("%s" % req.req) 32 | except AttributeError: 33 | # pip >= 20.0.2 34 | requirements.append(req.requirement) 35 | return requirements 36 | 37 | 38 | if __name__ == "__main__": 39 | HERE = path.abspath(path.dirname(__file__)) 40 | INSTALL_REQUIRES = get_requirements(path.join(HERE, "requirements.txt")) 41 | 42 | with io.open(path.join(HERE, "README.rst"), encoding="utf-8") as readme: 43 | LONG_DESCRIPTION = readme.read() 44 | 45 | def local_scheme(version): 46 | """Skip the local version (eg. +xyz of 0.6.1.dev4+gdf99fe2) 47 | to be able to upload to Test PyPI""" 48 | return "" 49 | 50 | setup( 51 | name="modoboa-dmarc", 52 | description="DMARC related tools for Modoboa", 53 | long_description=LONG_DESCRIPTION, 54 | license="MIT", 55 | url="http://modoboa.org/", 56 | author="Antoine Nguyen", 57 | author_email="tonio@ngyn.org", 58 | classifiers=[ 59 | "Development Status :: 4 - Beta", 60 | "Environment :: Web Environment", 61 | "Framework :: Django :: 2.2", 62 | "Intended Audience :: System Administrators", 63 | "License :: OSI Approved :: MIT License", 64 | "Operating System :: OS Independent", 65 | "Programming Language :: Python :: 3", 66 | "Programming Language :: Python :: 3.6", 67 | "Programming Language :: Python :: 3.7", 68 | "Programming Language :: Python :: 3.8", 69 | "Topic :: Communications :: Email", 70 | "Topic :: Internet :: WWW/HTTP", 71 | ], 72 | keywords="email dmarc", 73 | packages=find_packages(), 74 | include_package_data=True, 75 | zip_safe=False, 76 | install_requires=INSTALL_REQUIRES, 77 | use_scm_version={"local_scheme": local_scheme}, 78 | setup_requires=["setuptools_scm"], 79 | ) 80 | -------------------------------------------------------------------------------- /test-requirements.txt: -------------------------------------------------------------------------------- 1 | factory-boy<3.3.0 2 | testfixtures==7.1.0 3 | psycopg2>=2.5.4 4 | mysqlclient>=1.3.3 5 | -------------------------------------------------------------------------------- /test_project/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from __future__ import unicode_literals 4 | 5 | import os 6 | import sys 7 | 8 | if __name__ == "__main__": 9 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "test_project.settings") 10 | try: 11 | from django.core.management import execute_from_command_line 12 | except ImportError: 13 | # The above import may fail for some other reason. Ensure that the 14 | # issue is really that Django is missing to avoid masking other 15 | # exceptions on Python 2. 16 | try: 17 | import django # noqa 18 | except ImportError: 19 | raise ImportError( 20 | "Couldn't import Django. Are you sure it's installed and " 21 | "available on your PYTHONPATH environment variable? Did you " 22 | "forget to activate a virtual environment?" 23 | ) 24 | raise 25 | execute_from_command_line(sys.argv) 26 | -------------------------------------------------------------------------------- /test_project/test_project/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modoboa/modoboa-dmarc/5b8ac71be1787b9d9d71c447d335e63b86fd4cf6/test_project/test_project/__init__.py -------------------------------------------------------------------------------- /test_project/test_project/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for test_project project. 3 | 4 | Generated by 'django-admin startproject' using Django 1.11.8. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.11/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/1.11/ref/settings/ 11 | """ 12 | 13 | from logging.handlers import SysLogHandler 14 | import os 15 | 16 | from modoboa.test_settings import * # noqa 17 | 18 | 19 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 20 | BASE_DIR = os.path.realpath(os.path.dirname(os.path.dirname(__file__))) 21 | 22 | 23 | # Quick-start development settings - unsuitable for production 24 | # See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/ 25 | 26 | # SECURITY WARNING: keep the secret key used in production secret! 27 | SECRET_KEY = 'w537@nm@5n)=+e%-7*z-jxf21a#0k%uv^rbu**+cj4=_u57e(8' 28 | 29 | # SECURITY WARNING: don't run with debug turned on in production! 30 | DEBUG = 'DEBUG' in os.environ 31 | 32 | TEMPLATE_DEBUG = DEBUG 33 | 34 | ALLOWED_HOSTS = [ 35 | '127.0.0.1', 36 | 'localhost', 37 | ] 38 | 39 | SITE_ID = 1 40 | 41 | # Security settings 42 | 43 | X_FRAME_OPTIONS = "SAMEORIGIN" 44 | 45 | # Application definition 46 | 47 | INSTALLED_APPS = ( 48 | 'django.contrib.auth', 49 | 'django.contrib.contenttypes', 50 | 'django.contrib.sessions', 51 | 'django.contrib.messages', 52 | 'django.contrib.sites', 53 | 'django.contrib.staticfiles', 54 | 'reversion', 55 | 'ckeditor', 56 | 'ckeditor_uploader', 57 | 'rest_framework', 58 | 'rest_framework.authtoken', 59 | 'django_otp', 60 | 'django_otp.plugins.otp_totp', 61 | 'django_otp.plugins.otp_static', 62 | ) 63 | 64 | # A dedicated place to register Modoboa applications 65 | # Do not delete it. 66 | # Do not change the order. 67 | MODOBOA_APPS = ( 68 | 'modoboa', 69 | 'modoboa.core', 70 | 'modoboa.lib', 71 | 'modoboa.admin', 72 | 'modoboa.transport', 73 | 'modoboa.relaydomains', 74 | 'modoboa.limits', 75 | 'modoboa.parameters', 76 | # Modoboa extensions here. 77 | 'modoboa_dmarc', 78 | ) 79 | 80 | INSTALLED_APPS += MODOBOA_APPS 81 | 82 | AUTH_USER_MODEL = 'core.User' 83 | 84 | MIDDLEWARE = ( 85 | 'x_forwarded_for.middleware.XForwardedForMiddleware', 86 | 'django.contrib.sessions.middleware.SessionMiddleware', 87 | 'django.middleware.common.CommonMiddleware', 88 | 'django.middleware.csrf.CsrfViewMiddleware', 89 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 90 | 'django_otp.middleware.OTPMiddleware', 91 | 'modoboa.core.middleware.TwoFAMiddleware', 92 | 'django.contrib.messages.middleware.MessageMiddleware', 93 | 'django.middleware.locale.LocaleMiddleware', 94 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 95 | 'modoboa.core.middleware.LocalConfigMiddleware', 96 | 'modoboa.lib.middleware.AjaxLoginRedirect', 97 | 'modoboa.lib.middleware.CommonExceptionCatcher', 98 | 'modoboa.lib.middleware.RequestCatcherMiddleware', 99 | ) 100 | 101 | AUTHENTICATION_BACKENDS = ( 102 | # 'modoboa.lib.authbackends.LDAPBackend', 103 | # 'modoboa.lib.authbackends.SMTPBackend', 104 | 'django.contrib.auth.backends.ModelBackend', 105 | ) 106 | 107 | # SMTP authentication 108 | # AUTH_SMTP_SERVER_ADDRESS = 'localhost' 109 | # AUTH_SMTP_SERVER_PORT = 25 110 | # AUTH_SMTP_SECURED_MODE = None # 'ssl' or 'starttls' are accepted 111 | 112 | 113 | TEMPLATES = [ 114 | { 115 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 116 | 'DIRS': [], 117 | 'APP_DIRS': True, 118 | 'OPTIONS': { 119 | 'context_processors': [ 120 | 'django.template.context_processors.debug', 121 | 'django.template.context_processors.request', 122 | 'django.contrib.auth.context_processors.auth', 123 | 'django.template.context_processors.i18n', 124 | 'django.template.context_processors.media', 125 | 'django.template.context_processors.static', 126 | 'django.template.context_processors.tz', 127 | 'django.contrib.messages.context_processors.messages', 128 | 'modoboa.core.context_processors.top_notifications', 129 | ], 130 | 'debug': TEMPLATE_DEBUG, 131 | }, 132 | }, 133 | ] 134 | 135 | ROOT_URLCONF = 'test_project.urls' 136 | 137 | WSGI_APPLICATION = 'test_project.wsgi.application' 138 | 139 | 140 | # Internationalization 141 | # https://docs.djangoproject.com/en/1.11/topics/i18n/ 142 | 143 | LANGUAGE_CODE = 'en-us' 144 | 145 | TIME_ZONE = 'UTC' 146 | 147 | USE_I18N = True 148 | 149 | USE_L10N = True 150 | 151 | USE_TZ = True 152 | 153 | # Default primary key field type 154 | # https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field 155 | 156 | DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' 157 | 158 | # Static files (CSS, JavaScript, Images) 159 | # https://docs.djangoproject.com/en/1.11/howto/static-files/ 160 | 161 | STATIC_URL = '/sitestatic/' 162 | STATIC_ROOT = os.path.join(BASE_DIR, 'sitestatic') 163 | STATICFILES_DIRS = ( 164 | # os.path.join(BASE_DIR, '..', 'modoboa', 'bower_components'), 165 | ) 166 | 167 | MEDIA_URL = '/media/' 168 | MEDIA_ROOT = os.path.join(BASE_DIR, 'media') 169 | 170 | # Rest framework settings 171 | 172 | REST_FRAMEWORK = { 173 | 'DEFAULT_AUTHENTICATION_CLASSES': ( 174 | 'rest_framework.authentication.TokenAuthentication', 175 | 'rest_framework.authentication.SessionAuthentication', 176 | ), 177 | } 178 | 179 | # Modoboa settings 180 | # MODOBOA_CUSTOM_LOGO = os.path.join(MEDIA_URL, "custom_logo.png") 181 | 182 | # DOVECOT_LOOKUP_PATH = ('/path/to/dovecot', ) 183 | 184 | MODOBOA_API_URL = 'https://api.modoboa.org/1/' 185 | 186 | # Password validation 187 | # https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators 188 | 189 | AUTH_PASSWORD_VALIDATORS = [ 190 | { 191 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 192 | }, 193 | { 194 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 195 | }, 196 | { 197 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 198 | }, 199 | { 200 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 201 | }, 202 | { 203 | 'NAME': 'modoboa.core.password_validation.ComplexityValidator', 204 | 'OPTIONS': { 205 | 'upper': 1, 206 | 'lower': 1, 207 | 'digits': 1, 208 | 'specials': 0 209 | } 210 | }, 211 | ] 212 | 213 | # CKeditor 214 | 215 | CKEDITOR_UPLOAD_PATH = "uploads/" 216 | 217 | CKEDITOR_IMAGE_BACKEND = "pillow" 218 | 219 | CKEDITOR_RESTRICT_BY_USER = True 220 | 221 | CKEDITOR_BROWSE_SHOW_DIRS = True 222 | 223 | CKEDITOR_ALLOW_NONIMAGE_FILES = False 224 | 225 | CKEDITOR_CONFIGS = { 226 | 'default': { 227 | 'allowedContent': True, 228 | 'toolbar': 'Modoboa', 229 | 'width': None, 230 | 'toolbar_Modoboa': [ 231 | ['Bold', 'Italic', 'Underline'], 232 | ['JustifyLeft', 'JustifyCenter', 'JustifyRight', 'JustifyBlock'], 233 | ['BidiLtr', 'BidiRtl', 'Language'], 234 | ['NumberedList', 'BulletedList', '-', 'Outdent', 'Indent'], 235 | ['Undo', 'Redo'], 236 | ['Link', 'Unlink', 'Anchor', '-', 'Smiley'], 237 | ['TextColor', 'BGColor', '-', 'Source'], 238 | ['Font', 'FontSize'], 239 | ['Image', ], 240 | ['SpellChecker'] 241 | ], 242 | }, 243 | } 244 | 245 | # Logging configuration 246 | 247 | LOGGING = { 248 | 'version': 1, 249 | 'formatters': { 250 | 'syslog': { 251 | 'format': '%(name)s: %(levelname)s %(message)s' 252 | }, 253 | }, 254 | 'handlers': { 255 | 'syslog-auth': { 256 | 'class': 'logging.handlers.SysLogHandler', 257 | 'facility': SysLogHandler.LOG_AUTH, 258 | 'formatter': 'syslog' 259 | }, 260 | 'modoboa': { 261 | 'class': 'modoboa.core.loggers.SQLHandler', 262 | } 263 | }, 264 | 'loggers': { 265 | 'modoboa.auth': { 266 | 'handlers': ['syslog-auth', 'modoboa'], 267 | 'level': 'INFO', 268 | 'propagate': False 269 | }, 270 | 'modoboa.admin': { 271 | 'handlers': ['modoboa'], 272 | 'level': 'INFO', 273 | 'propagate': False 274 | } 275 | } 276 | } 277 | 278 | # Load settings from extensions 279 | -------------------------------------------------------------------------------- /test_project/test_project/urls.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | from django.conf.urls import include, url 4 | 5 | urlpatterns = [ 6 | url(r"", include("modoboa.urls")), 7 | ] 8 | -------------------------------------------------------------------------------- /test_project/test_project/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for test_project project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.11/howto/deployment/wsgi/ 8 | """ 9 | 10 | from __future__ import unicode_literals 11 | 12 | import os 13 | 14 | from django.core.wsgi import get_wsgi_application 15 | 16 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "test_project.settings") 17 | 18 | application = get_wsgi_application() 19 | --------------------------------------------------------------------------------