modoboa 2.4.11__py3-none-any.whl → 2.5.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- modoboa/admin/api/v2/serializers.py +14 -0
- modoboa/amavis/__init__.py +3 -0
- modoboa/amavis/app_settings.py +276 -0
- modoboa/amavis/apps.py +18 -0
- modoboa/amavis/checks/__init__.py +2 -0
- modoboa/amavis/checks/settings_checks.py +59 -0
- modoboa/amavis/dbrouter.py +35 -0
- modoboa/amavis/factories.py +164 -0
- modoboa/amavis/handlers.py +146 -0
- modoboa/amavis/lib.py +381 -0
- modoboa/amavis/management/__init__.py +0 -0
- modoboa/amavis/management/commands/__init__.py +0 -0
- modoboa/amavis/management/commands/amnotify.py +99 -0
- modoboa/amavis/management/commands/qcleanup.py +84 -0
- modoboa/amavis/migrations/0001_initial.py +340 -0
- modoboa/amavis/migrations/__init__.py +0 -0
- modoboa/amavis/models.py +226 -0
- modoboa/amavis/serializers.py +139 -0
- modoboa/amavis/sql_connector.py +240 -0
- modoboa/amavis/sql_email.py +66 -0
- modoboa/amavis/tasks.py +33 -0
- modoboa/amavis/templates/amavis/notifications/pending_requests.html +16 -0
- modoboa/amavis/tests/__init__.py +0 -0
- modoboa/amavis/tests/sa-learn +3 -0
- modoboa/amavis/tests/sample_messages/quarantined-input.txt +80 -0
- modoboa/amavis/tests/sample_messages/quarantined-output-plain_nolinks.txt +17 -0
- modoboa/amavis/tests/spamc +3 -0
- modoboa/amavis/tests/test_checks.py +25 -0
- modoboa/amavis/tests/test_handlers.py +214 -0
- modoboa/amavis/tests/test_lib.py +90 -0
- modoboa/amavis/tests/test_management_commands.py +45 -0
- modoboa/amavis/tests/test_sql_email.py +67 -0
- modoboa/amavis/tests/test_utils.py +19 -0
- modoboa/amavis/tests/test_viewsets.py +319 -0
- modoboa/amavis/urls.py +11 -0
- modoboa/amavis/utils.py +105 -0
- modoboa/amavis/viewsets.py +265 -0
- modoboa/core/api/v1/serializers.py +7 -5
- modoboa/core/api/v2/serializers.py +4 -2
- modoboa/core/api/v2/tests.py +16 -4
- modoboa/core/api/v2/urls.py +5 -5
- modoboa/core/api/v2/views.py +6 -2
- modoboa/core/api/v2/viewsets.py +24 -3
- modoboa/core/commands/deploy.py +3 -0
- modoboa/core/commands/templates/settings.py.tpl +12 -11
- modoboa/core/handlers.py +6 -2
- modoboa/core/management/commands/add_allowed_hosts.py +33 -0
- modoboa/core/management/commands/load_initial_data.py +10 -0
- modoboa/core/migrations/0025_rename_user_email_is_active_core_user_email_c0c03f_idx.py +23 -5
- modoboa/core/tests/test_core.py +24 -0
- modoboa/core/utils.py +3 -0
- modoboa/frontend_dist/assets/AccountAliasForm-C0oHHyZL.js +1 -0
- modoboa/frontend_dist/assets/AccountEditView-lgSJ2Se8.js +1 -0
- modoboa/frontend_dist/assets/AccountLayout-U386K8zy.js +1 -0
- modoboa/frontend_dist/assets/AccountPasswordSubForm-YsaE_cDx.js +1 -0
- modoboa/frontend_dist/assets/AccountView-1jfKFDwb.js +1 -0
- modoboa/frontend_dist/assets/AddressBook-CwN64Zls.js +1 -0
- modoboa/frontend_dist/assets/AdminLayout-Cxm1lggg.js +1 -0
- modoboa/frontend_dist/assets/AlarmsView-9yKGbmkC.css +1 -0
- modoboa/frontend_dist/assets/AlarmsView-Bcjsicac.js +1 -0
- modoboa/frontend_dist/assets/AliasEditView-k3rVt1tG.js +1 -0
- modoboa/frontend_dist/assets/{AliasRecipientForm-IOae6sjF.js → AliasRecipientForm-IehUzKok.js} +1 -1
- modoboa/frontend_dist/assets/AliasView-DMzA10eD.js +1 -0
- modoboa/frontend_dist/assets/AuditTrailView-5dnGX5El.js +1 -0
- modoboa/frontend_dist/assets/CalendarView-DZONeDA9.js +1 -0
- modoboa/frontend_dist/assets/{ChoiceField-DJ_c78Cm.js → ChoiceField-DnwXRkht.js} +1 -1
- modoboa/frontend_dist/assets/ComposeEmailForm-kghmfNuE.js +1 -0
- modoboa/frontend_dist/assets/ComposeEmailView-DLv3wk1k.js +1 -0
- modoboa/frontend_dist/assets/ConfirmDialog-CcPrCKuI.js +1 -0
- modoboa/frontend_dist/assets/{ConnectedLayout-Dvwmicnc.css → ConnectedLayout-Bxh21hcH.css} +1 -1
- modoboa/frontend_dist/assets/ConnectedLayout-CWlBK7Hf.js +1 -0
- modoboa/frontend_dist/assets/CreationForm-CW4lxnPg.js +1 -0
- modoboa/frontend_dist/assets/DashboardView-DXVZMbMo.js +1 -0
- modoboa/frontend_dist/assets/DomainAdminList-C3jcDDc3.js +1 -0
- modoboa/frontend_dist/assets/DomainEditView-ph8AaElX.js +1 -0
- modoboa/frontend_dist/assets/{DomainTransportForm-C2xo0Yd7.js → DomainTransportForm-NCz6Bl-h.js} +1 -1
- modoboa/frontend_dist/assets/DomainView-BgMSSuU-.js +5 -0
- modoboa/frontend_dist/assets/{DomainView-BDKoBFYr.css → DomainView-CCLYXPHx.css} +1 -1
- modoboa/frontend_dist/assets/DomainsView-CEEU9btK.js +1 -0
- modoboa/frontend_dist/assets/DomainsView-DZ-ss9bI.css +1 -0
- modoboa/frontend_dist/assets/EmailField-DeqDPm5j.js +1 -0
- modoboa/frontend_dist/assets/EmailView-DczVhVO0.js +1 -0
- modoboa/frontend_dist/assets/EmptyLayout-BXgcfMLH.js +1 -0
- modoboa/frontend_dist/assets/FiltersView-nJj_gSCx.js +1 -0
- modoboa/frontend_dist/assets/ForwardEmailView-Bgv3JQb6.js +1 -0
- modoboa/frontend_dist/assets/{HtmlEditor-CJ9umKeO.js → HtmlEditor-BWRdelVw.js} +1 -1
- modoboa/frontend_dist/assets/{IdentitiesView-0ziuQ5s-.css → IdentitiesView-DPrrRMS5.css} +1 -1
- modoboa/frontend_dist/assets/IdentitiesView-Dld9IloZ.js +1 -0
- modoboa/frontend_dist/assets/InformationView-BBWKSX8D.js +1 -0
- modoboa/frontend_dist/assets/InformationView-C9vvvQhJ.css +1 -0
- modoboa/frontend_dist/assets/{LoadingData-CYwX3Jpn.js → LoadingData-G57nJ_JV.js} +1 -1
- modoboa/frontend_dist/assets/{LoginCallbackView-E01qkKn0.js → LoginCallbackView-DjyE2SG_.js} +1 -1
- modoboa/frontend_dist/assets/{LoginView-Cy4uFV9h.js → LoginView-CqCCXYLo.js} +1 -1
- modoboa/frontend_dist/assets/{MailboxView-B-aI4XBq.css → MailboxView-CfStlWhk.css} +1 -1
- modoboa/frontend_dist/assets/MailboxView-DRrs9eLO.js +1 -0
- modoboa/frontend_dist/assets/MenuItems-BqIZW5av.js +1 -0
- modoboa/frontend_dist/assets/MessageView-D_6tx_gd.js +1 -0
- modoboa/frontend_dist/assets/MessagesView-BH7JIR03.js +1 -0
- modoboa/frontend_dist/assets/MigrationsView-Cv_So9T-.js +1 -0
- modoboa/frontend_dist/assets/{ParametersForm-BZM0QSvg.js → ParametersForm-3qXttTuQ.js} +1 -1
- modoboa/frontend_dist/assets/ParametersView-3Ns04cpQ.js +1 -0
- modoboa/frontend_dist/assets/ParametersView-B5B5Dt6K.js +1 -0
- modoboa/frontend_dist/assets/ProviderEditView-zh7CY832.js +1 -0
- modoboa/frontend_dist/assets/ProviderGeneralForm-BQU7t3ma.js +1 -0
- modoboa/frontend_dist/assets/ProvidersView-CoF_ZkZA.js +1 -0
- modoboa/frontend_dist/assets/QuarantineLayout-CYBsrbJM.js +1 -0
- modoboa/frontend_dist/assets/QuarantineView-D4gOE4EQ.css +1 -0
- modoboa/frontend_dist/assets/QuarantineView-DNvpoycb.js +1 -0
- modoboa/frontend_dist/assets/ReplyEmailView-D1XTcglu.js +1 -0
- modoboa/frontend_dist/assets/ResourcesForm-BW8rUGgZ.js +1 -0
- modoboa/frontend_dist/assets/SelfServiceLayout-DfDHiYeX.js +1 -0
- modoboa/frontend_dist/assets/{SettingsView-BxLJBFY0.js → SettingsView-gQiJ2NVb.js} +2 -2
- modoboa/frontend_dist/assets/StatisticsView-DYalet_q.js +1 -0
- modoboa/frontend_dist/assets/TimeSerieChart-BZ2htbFk.js +1 -0
- modoboa/frontend_dist/assets/UserLayout-zUtHi-z-.js +1 -0
- modoboa/frontend_dist/assets/VAlert-4r6LxKtg.js +1 -0
- modoboa/frontend_dist/assets/VApp-CX_C7AUN.js +1 -0
- modoboa/frontend_dist/assets/VAutocomplete-DNKmBvyZ.js +1 -0
- modoboa/frontend_dist/assets/VAvatar-DbuoZWmf.js +1 -0
- modoboa/frontend_dist/assets/VBadge-BQrRJ9S0.css +1 -0
- modoboa/frontend_dist/assets/VBadge-Bv2nvUmC.js +1 -0
- modoboa/frontend_dist/assets/VCard-DzjUT5OP.js +1 -0
- modoboa/frontend_dist/assets/VCheckbox-dr7UFjl4.js +1 -0
- modoboa/frontend_dist/assets/{VCheckboxBtn-Dt810gWf.js → VCheckboxBtn-CpFdBnTv.js} +1 -1
- modoboa/frontend_dist/assets/VChip-CaQvfmkw.js +1 -0
- modoboa/frontend_dist/assets/VColorPicker-ByGpCW5O.js +1 -0
- modoboa/frontend_dist/assets/{VContainer-DvTbsotR.js → VContainer-74Dnn8Ux.js} +1 -1
- modoboa/frontend_dist/assets/VDataTable-CL7yHvG7.js +1 -0
- modoboa/frontend_dist/assets/VDataTableServer-BqvNcIdw.js +1 -0
- modoboa/frontend_dist/assets/VDataTableVirtual--KsOP8i6.js +1 -0
- modoboa/frontend_dist/assets/{VDialog-Bk6EWNhz.js → VDialog-DmTGCGR0.js} +1 -1
- modoboa/frontend_dist/assets/VExpansionPanels-B7sSTCwd.js +1 -0
- modoboa/frontend_dist/assets/VFileInput-SULIc6F3.js +1 -0
- modoboa/frontend_dist/assets/VForm-DsRLc-sa.js +1 -0
- modoboa/frontend_dist/assets/VInput-CcxkaOXT.css +1 -0
- modoboa/frontend_dist/assets/VInput-DVKUObZe.js +1 -0
- modoboa/frontend_dist/assets/VMenu-nv0XOgg0.js +1 -0
- modoboa/frontend_dist/assets/VPicker-DnDSWJHJ.js +1 -0
- modoboa/frontend_dist/assets/VProgressCircular-qK6p5X_Y.js +1 -0
- modoboa/frontend_dist/assets/VRadioGroup-CbiPLy0t.js +1 -0
- modoboa/frontend_dist/assets/{VRow-BF35mT1S.js → VRow-DJ0NB63-.js} +1 -1
- modoboa/frontend_dist/assets/VSelect-CxCFMHyF.js +1 -0
- modoboa/frontend_dist/assets/VSelectionControl-C-6A4us5.js +1 -0
- modoboa/frontend_dist/assets/VSheet-DI6SxLnG.js +1 -0
- modoboa/frontend_dist/assets/VSpacer-CoJVmx8k.js +1 -0
- modoboa/frontend_dist/assets/VSwitch-DPnjPQuU.js +1 -0
- modoboa/frontend_dist/assets/VTable-ldTxgQPW.js +1 -0
- modoboa/frontend_dist/assets/VTabs-aS8WSL9I.js +1 -0
- modoboa/frontend_dist/assets/VTextField-BzBVKKob.css +1 -0
- modoboa/frontend_dist/assets/VTextField-DKbr4H5w.js +1 -0
- modoboa/frontend_dist/assets/VTextarea-BttkFsM4.js +1 -0
- modoboa/frontend_dist/assets/VToolbar-BxX3W2kR.js +1 -0
- modoboa/frontend_dist/assets/VWindowItem-Cvqvdegd.js +1 -0
- modoboa/frontend_dist/assets/WebmailLayout-BT2k6U7q.js +1 -0
- modoboa/frontend_dist/assets/accounts-CC2F0a0c.js +1 -0
- modoboa/frontend_dist/assets/{admin-o-HRGnmT.js → admin-CHCHFGI6.js} +1 -1
- modoboa/frontend_dist/assets/{aliases-DDVeehyg.js → aliases-C9bUD4Ws.js} +1 -1
- modoboa/frontend_dist/assets/amavis-BhzV4rgf.js +1 -0
- modoboa/frontend_dist/assets/amavis-DCVJxuui.js +1 -0
- modoboa/frontend_dist/assets/{contacts-C84DY-Q1.js → contacts-Dxz6eWpf.js} +1 -1
- modoboa/frontend_dist/assets/{domains-Bgn4ixHL.js → domains-C2cornvL.js} +1 -1
- modoboa/frontend_dist/assets/{domains.store-DTE-V7Y1.js → domains.store-BLKRipG8.js} +1 -1
- modoboa/frontend_dist/assets/{filter-CnffiQAW.js → filter-rmxrcjKk.js} +1 -1
- modoboa/frontend_dist/assets/forwardRefs-CpzzjgpX.js +1 -0
- modoboa/frontend_dist/assets/global.store-DndbMXYb.js +1 -0
- modoboa/frontend_dist/assets/{importExport-BlQYb0NO.js → importExport-C3uqrcok.js} +1 -1
- modoboa/frontend_dist/assets/index-LhNzkzAh.js +984 -0
- modoboa/frontend_dist/assets/layout-DbjDe3Wl.js +1 -0
- modoboa/frontend_dist/assets/{layout.store-DkjrAoXt.js → layout.store-Vq5mvIp7.js} +1 -1
- modoboa/frontend_dist/assets/{logos-q8SEyAa4.js → logos-Bvcy0usu.js} +1 -1
- modoboa/frontend_dist/assets/{logs-B7IJ7LBa.js → logs-BuItINky.js} +1 -1
- modoboa/frontend_dist/assets/{parameters-A6iBEYQq.js → parameters-C8IYEP7q.js} +1 -1
- modoboa/frontend_dist/assets/{parameters.store-BiXS4_6w.js → parameters.store-1cwSP2JP.js} +1 -1
- modoboa/frontend_dist/assets/permissions-DQjAcO9S.js +1 -0
- modoboa/frontend_dist/assets/{ssrBoot-AzTdjPjk.js → ssrBoot-BxIQ9ccA.js} +1 -1
- modoboa/frontend_dist/assets/{tag-BnSYRTcD.js → tag-3cfI1_f7.js} +1 -1
- modoboa/frontend_dist/assets/transports-D4Jk4-AP.js +1 -0
- modoboa/frontend_dist/assets/{webmail-CSH_3l6R.js → webmail-B2IUjaxM.js} +1 -1
- modoboa/frontend_dist/index.html +1 -1
- modoboa/lib/email_utils.py +2 -2
- modoboa/lib/permissions.py +7 -0
- modoboa/lib/test_runners.py +29 -0
- modoboa/lib/tests/__init__.py +5 -1
- modoboa/locale/br/LC_MESSAGES/django.po +87 -75
- modoboa/locale/cs/LC_MESSAGES/django.po +82 -74
- modoboa/locale/cs_CZ/LC_MESSAGES/django.po +145 -121
- modoboa/locale/de/LC_MESSAGES/django.mo +0 -0
- modoboa/locale/de/LC_MESSAGES/django.po +339 -651
- modoboa/locale/de_DE/LC_MESSAGES/django.po +87 -75
- modoboa/locale/el_GR/LC_MESSAGES/django.po +160 -135
- modoboa/locale/en/LC_MESSAGES/django.po +82 -74
- modoboa/locale/es/LC_MESSAGES/django.po +158 -131
- modoboa/locale/es_MX/LC_MESSAGES/django.po +82 -74
- modoboa/locale/fi/LC_MESSAGES/django.po +87 -75
- modoboa/locale/fr/LC_MESSAGES/django.mo +0 -0
- modoboa/locale/fr/LC_MESSAGES/django.po +469 -201
- modoboa/locale/hu/LC_MESSAGES/django.po +82 -74
- modoboa/locale/it/LC_MESSAGES/django.mo +0 -0
- modoboa/locale/it/LC_MESSAGES/django.po +148 -122
- modoboa/locale/ja_JP/LC_MESSAGES/django.mo +0 -0
- modoboa/locale/ja_JP/LC_MESSAGES/django.po +80 -72
- modoboa/locale/ka/LC_MESSAGES/django.po +82 -74
- modoboa/locale/nl_NL/LC_MESSAGES/django.po +160 -132
- modoboa/locale/no/LC_MESSAGES/django.po +82 -74
- modoboa/locale/pl_PL/LC_MESSAGES/django.mo +0 -0
- modoboa/locale/pl_PL/LC_MESSAGES/django.po +172 -149
- modoboa/locale/pt_BR/LC_MESSAGES/django.mo +0 -0
- modoboa/locale/pt_BR/LC_MESSAGES/django.po +172 -144
- modoboa/locale/pt_PT/LC_MESSAGES/django.po +135 -112
- modoboa/locale/ro_RO/LC_MESSAGES/django.po +87 -75
- modoboa/locale/ru/LC_MESSAGES/django.po +142 -118
- modoboa/locale/si/LC_MESSAGES/django.po +82 -74
- modoboa/locale/sk/LC_MESSAGES/django.po +82 -74
- modoboa/locale/sk_SK/LC_MESSAGES/django.po +84 -76
- modoboa/locale/sl_SI/LC_MESSAGES/django.po +90 -76
- modoboa/locale/sv/LC_MESSAGES/django.mo +0 -0
- modoboa/locale/sv/LC_MESSAGES/django.po +172 -139
- modoboa/locale/tr/LC_MESSAGES/django.po +87 -75
- modoboa/locale/tr_TR/LC_MESSAGES/django.po +84 -74
- modoboa/locale/uk/LC_MESSAGES/django.po +82 -74
- modoboa/locale/zh/LC_MESSAGES/django.po +82 -74
- modoboa/locale/zh_CN/LC_MESSAGES/django.po +82 -74
- modoboa/locale/zh_TW/LC_MESSAGES/django.po +87 -75
- modoboa/parameters/api/v2/tests.py +2 -2
- modoboa/parameters/api/v2/viewsets.py +2 -0
- modoboa/policyd/tests.py +2 -0
- modoboa/urls_api_v2.py +6 -0
- modoboa/webmail/lib/imaputils.py +2 -2
- {modoboa-2.4.11.dist-info → modoboa-2.5.1.dist-info}/METADATA +6 -4
- {modoboa-2.4.11.dist-info → modoboa-2.5.1.dist-info}/RECORD +235 -185
- modoboa/frontend_dist/assets/AccountAliasForm-BV6KvTu6.js +0 -1
- modoboa/frontend_dist/assets/AccountEditView-DDOFyfBD.js +0 -1
- modoboa/frontend_dist/assets/AccountLayout-rX51xgxT.js +0 -1
- modoboa/frontend_dist/assets/AccountPasswordSubForm-D9S6LaeH.js +0 -1
- modoboa/frontend_dist/assets/AccountView-cmvaZNq3.js +0 -1
- modoboa/frontend_dist/assets/AddressBook-DCJxL8SU.js +0 -1
- modoboa/frontend_dist/assets/AdminLayout-r0wfG2lO.js +0 -1
- modoboa/frontend_dist/assets/AlarmsView-Bheey-gp.css +0 -1
- modoboa/frontend_dist/assets/AlarmsView-C0bqC4PA.js +0 -1
- modoboa/frontend_dist/assets/AliasEditView-DVoWoCGY.js +0 -1
- modoboa/frontend_dist/assets/AliasView-DrONZXOh.js +0 -1
- modoboa/frontend_dist/assets/AuditTrailView-OTkoZaMU.js +0 -1
- modoboa/frontend_dist/assets/CalendarView-CqF4_Ui9.js +0 -1
- modoboa/frontend_dist/assets/ComposeEmailForm-DO5_GB3e.js +0 -1
- modoboa/frontend_dist/assets/ComposeEmailView-A91HCBsN.js +0 -1
- modoboa/frontend_dist/assets/ConfirmDialog-BBcgdAnO.js +0 -1
- modoboa/frontend_dist/assets/ConnectedLayout-1oRW-Rql.js +0 -1
- modoboa/frontend_dist/assets/CreationForm-71YJbjsA.js +0 -1
- modoboa/frontend_dist/assets/DashboardView-CdLpSfUl.js +0 -1
- modoboa/frontend_dist/assets/DomainAdminList-BjC4KsqI.js +0 -1
- modoboa/frontend_dist/assets/DomainEditView-CQjKwYxl.js +0 -1
- modoboa/frontend_dist/assets/DomainView-BhhuZI_N.js +0 -5
- modoboa/frontend_dist/assets/DomainsView-Cft4BP8Z.js +0 -1
- modoboa/frontend_dist/assets/DomainsView-DasJ0NdZ.css +0 -1
- modoboa/frontend_dist/assets/EmailField-C8umy0EU.js +0 -1
- modoboa/frontend_dist/assets/EmailView-ki7uEQPD.js +0 -1
- modoboa/frontend_dist/assets/EmptyLayout-DaA1XH9n.js +0 -1
- modoboa/frontend_dist/assets/FiltersView-FYFZxG4B.js +0 -1
- modoboa/frontend_dist/assets/ForwardEmailView-cUbnSYCF.js +0 -1
- modoboa/frontend_dist/assets/IdentitiesView-njNo8N5n.js +0 -1
- modoboa/frontend_dist/assets/InformationView-D1h38POt.js +0 -1
- modoboa/frontend_dist/assets/InformationView-U5Ww-Sx1.css +0 -1
- modoboa/frontend_dist/assets/MailboxView-IlrLWm_H.js +0 -1
- modoboa/frontend_dist/assets/MenuItems-BAtHWzAE.js +0 -1
- modoboa/frontend_dist/assets/MessagesView-OSpjixFq.js +0 -1
- modoboa/frontend_dist/assets/MigrationsView-DKNOsVzF.js +0 -1
- modoboa/frontend_dist/assets/ParametersView-C4bXASiq.js +0 -1
- modoboa/frontend_dist/assets/ParametersView-CYXgNmc1.js +0 -1
- modoboa/frontend_dist/assets/ProviderEditView-CyxCWTST.js +0 -1
- modoboa/frontend_dist/assets/ProviderGeneralForm-BYPjNHqB.js +0 -1
- modoboa/frontend_dist/assets/ProvidersView-CxrMkRyk.js +0 -1
- modoboa/frontend_dist/assets/ReplyEmailView-Dkw9-N26.js +0 -1
- modoboa/frontend_dist/assets/ResourcesForm-CuUvrOdY.js +0 -1
- modoboa/frontend_dist/assets/StatisticsView-BN7QsZMT.js +0 -1
- modoboa/frontend_dist/assets/TimeSerieChart-BMN8BeFZ.js +0 -1
- modoboa/frontend_dist/assets/UserLayout-B6-JQg4F.js +0 -1
- modoboa/frontend_dist/assets/VAlert-DIQTrRif.js +0 -1
- modoboa/frontend_dist/assets/VApp-CpkYA7js.js +0 -1
- modoboa/frontend_dist/assets/VAutocomplete-C4IpXyl8.js +0 -1
- modoboa/frontend_dist/assets/VAvatar-Lpb-Dion.js +0 -1
- modoboa/frontend_dist/assets/VCard-er_isjE_.js +0 -1
- modoboa/frontend_dist/assets/VCheckbox-D-u8JXP1.js +0 -1
- modoboa/frontend_dist/assets/VChip-B4iSpj8_.js +0 -1
- modoboa/frontend_dist/assets/VColorPicker-BAjGDsXv.js +0 -1
- modoboa/frontend_dist/assets/VDataTable-4JRjbtgF.js +0 -1
- modoboa/frontend_dist/assets/VDataTableServer-tIDT1m3-.js +0 -1
- modoboa/frontend_dist/assets/VDataTableVirtual-BlnO18u_.js +0 -1
- modoboa/frontend_dist/assets/VExpansionPanels-CwGtXDhr.js +0 -1
- modoboa/frontend_dist/assets/VFileInput-D1_7ZkO_.js +0 -1
- modoboa/frontend_dist/assets/VForm-DAkW4nfy.js +0 -1
- modoboa/frontend_dist/assets/VMenu-BPFJwj2f.js +0 -1
- modoboa/frontend_dist/assets/VPicker-CfT82M8N.js +0 -1
- modoboa/frontend_dist/assets/VProgressCircular-w75-3ogi.js +0 -1
- modoboa/frontend_dist/assets/VRadioGroup-0j6DNC_k.js +0 -1
- modoboa/frontend_dist/assets/VSelect-Cs4ARbAS.js +0 -1
- modoboa/frontend_dist/assets/VSelectionControl-Dg-XyRRS.js +0 -1
- modoboa/frontend_dist/assets/VSheet-Btq_Mu4s.js +0 -1
- modoboa/frontend_dist/assets/VSpacer-C7xukQmu.js +0 -1
- modoboa/frontend_dist/assets/VSwitch-Cs1NQrmk.js +0 -1
- modoboa/frontend_dist/assets/VTable-CNz2SGk4.js +0 -1
- modoboa/frontend_dist/assets/VTabs-B1fyVn4M.js +0 -1
- modoboa/frontend_dist/assets/VTextField-BdyvgvkG.js +0 -1
- modoboa/frontend_dist/assets/VTextField-C-J20yj_.css +0 -1
- modoboa/frontend_dist/assets/VTextarea-DnOMpe0Q.js +0 -1
- modoboa/frontend_dist/assets/VToolbar-BiCiBxBJ.js +0 -1
- modoboa/frontend_dist/assets/VWindowItem-ChWm_kz3.js +0 -1
- modoboa/frontend_dist/assets/WebmailLayout-o4uEkp9e.js +0 -1
- modoboa/frontend_dist/assets/forwardRefs-Dvjn_Xq4.js +0 -1
- modoboa/frontend_dist/assets/global.store-BaiD63EN.js +0 -1
- modoboa/frontend_dist/assets/index-I1VDlN4g.js +0 -984
- modoboa/frontend_dist/assets/layout-D8ZJPiJ_.js +0 -1
- modoboa/frontend_dist/assets/permissions-CITHLHVg.js +0 -1
- modoboa/frontend_dist/assets/transports-Dz7c6kIy.js +0 -1
- {modoboa-2.4.11.data → modoboa-2.5.1.data}/scripts/modoboa-admin.py +0 -0
- {modoboa-2.4.11.dist-info → modoboa-2.5.1.dist-info}/WHEEL +0 -0
- {modoboa-2.4.11.dist-info → modoboa-2.5.1.dist-info}/entry_points.txt +0 -0
- {modoboa-2.4.11.dist-info → modoboa-2.5.1.dist-info}/licenses/LICENSE +0 -0
- {modoboa-2.4.11.dist-info → modoboa-2.5.1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
"""Amavis tests."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
from django.test import override_settings
|
|
6
|
+
from django.urls import reverse
|
|
7
|
+
|
|
8
|
+
from modoboa.admin import factories as admin_factories, models as admin_models
|
|
9
|
+
from modoboa.core import models as core_models
|
|
10
|
+
from modoboa.lib.tests import ModoAPITestCase
|
|
11
|
+
from modoboa.transport import factories as tr_factories
|
|
12
|
+
from .. import factories, lib, models
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class DomainTestCase(ModoAPITestCase):
|
|
16
|
+
"""Check that database is populated."""
|
|
17
|
+
|
|
18
|
+
databases = "__all__"
|
|
19
|
+
|
|
20
|
+
def setUp(self):
|
|
21
|
+
"""Initiate test context."""
|
|
22
|
+
self.admin = core_models.User.objects.get(username="admin")
|
|
23
|
+
|
|
24
|
+
def test_create_domain(self):
|
|
25
|
+
"""Test domain creation."""
|
|
26
|
+
domain = admin_factories.DomainFactory(name="domain.test")
|
|
27
|
+
name = f"@{domain.name}"
|
|
28
|
+
policy = models.Policy.objects.get(policy_name=name)
|
|
29
|
+
user = models.Users.objects.filter(policy=policy).first()
|
|
30
|
+
self.assertIsNot(user, None)
|
|
31
|
+
self.assertEqual(user.email, name)
|
|
32
|
+
|
|
33
|
+
# Create a domain alias
|
|
34
|
+
self.client.force_authenticate(self.admin)
|
|
35
|
+
url = reverse("v2:domain_alias-list")
|
|
36
|
+
data = {"name": "dalias.test", "target": domain.pk}
|
|
37
|
+
resp = self.client.post(url, data, format="json")
|
|
38
|
+
self.assertEqual(resp.status_code, 201)
|
|
39
|
+
name = "@dalias.test"
|
|
40
|
+
self.assertFalse(models.Policy.objects.filter(policy_name=name).exists())
|
|
41
|
+
user = models.Users.objects.get(email=name)
|
|
42
|
+
self.assertEqual(user.policy, policy)
|
|
43
|
+
|
|
44
|
+
# Delete domain alias
|
|
45
|
+
url = reverse("v2:domain_alias-detail", args=[resp.json()["pk"]])
|
|
46
|
+
resp = self.client.delete(url)
|
|
47
|
+
self.assertEqual(resp.status_code, 204)
|
|
48
|
+
self.assertFalse(models.Users.objects.filter(email=name).exists())
|
|
49
|
+
|
|
50
|
+
def test_rename_domain(self):
|
|
51
|
+
"""Test domain rename."""
|
|
52
|
+
domain = admin_factories.DomainFactory(name="domain.test")
|
|
53
|
+
domain.name = "domain1.test"
|
|
54
|
+
domain.save()
|
|
55
|
+
name = f"@{domain.name}"
|
|
56
|
+
self.assertTrue(models.Users.objects.filter(email=name).exists())
|
|
57
|
+
self.assertTrue(models.Policy.objects.filter(policy_name=name).exists())
|
|
58
|
+
|
|
59
|
+
# Now from form
|
|
60
|
+
self.client.force_authenticate(self.admin)
|
|
61
|
+
rdomain = admin_factories.DomainFactory(name="domain.relay", type="relaydomain")
|
|
62
|
+
rdomain.transport = tr_factories.TransportFactory(
|
|
63
|
+
pattern=rdomain.name,
|
|
64
|
+
service="relay",
|
|
65
|
+
_settings={
|
|
66
|
+
"relay_target_host": "external.host.tld",
|
|
67
|
+
"relay_target_port": "25",
|
|
68
|
+
"relay_verify_recipients": False,
|
|
69
|
+
},
|
|
70
|
+
)
|
|
71
|
+
rdomain.save()
|
|
72
|
+
url = reverse("v2:domain-detail", args=[rdomain.pk])
|
|
73
|
+
values = {
|
|
74
|
+
"name": "domain2.relay",
|
|
75
|
+
"quota": rdomain.quota,
|
|
76
|
+
"default_mailbox_quota": rdomain.default_mailbox_quota,
|
|
77
|
+
"type": "relaydomain",
|
|
78
|
+
"enabled": rdomain.enabled,
|
|
79
|
+
"transport": {
|
|
80
|
+
"service": rdomain.transport.service,
|
|
81
|
+
"settings": {
|
|
82
|
+
"relay_target_host": "127.0.0.1",
|
|
83
|
+
"relay_target_port": 25,
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
}
|
|
87
|
+
resp = self.client.put(url, values, format="json")
|
|
88
|
+
self.assertTrue(resp.status_code, 200)
|
|
89
|
+
|
|
90
|
+
def test_delete_domain(self):
|
|
91
|
+
"""Test domain removal."""
|
|
92
|
+
domain = admin_factories.DomainFactory(name="domain.test")
|
|
93
|
+
domain.delete(None)
|
|
94
|
+
name = f"@{domain.name}"
|
|
95
|
+
self.assertFalse(models.Users.objects.filter(email=name).exists())
|
|
96
|
+
self.assertFalse(models.Policy.objects.filter(policy_name=name).exists())
|
|
97
|
+
|
|
98
|
+
def test_update_domain_policy(self):
|
|
99
|
+
"""Check domain policy edition."""
|
|
100
|
+
self.client.force_authenticate(self.admin)
|
|
101
|
+
domain = admin_factories.DomainFactory(name="domain.test")
|
|
102
|
+
url = reverse("v2:amavis-policy-detail", args=[domain.pk])
|
|
103
|
+
custom_title = "This is SPAM!"
|
|
104
|
+
data = {"bypass_virus_checks": "Y", "spam_subject_tag2": custom_title}
|
|
105
|
+
resp = self.client.patch(url, data, format="json")
|
|
106
|
+
self.assertEqual(resp.status_code, 200)
|
|
107
|
+
name = f"@{domain.name}"
|
|
108
|
+
policy = models.Policy.objects.get(policy_name=name)
|
|
109
|
+
self.assertEqual(policy.spam_subject_tag2, custom_title)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
@override_settings(SA_LOOKUP_PATH=(os.path.dirname(__file__),))
|
|
113
|
+
class ManualLearningTestCase(ModoAPITestCase):
|
|
114
|
+
"""Check manual learning mode."""
|
|
115
|
+
|
|
116
|
+
databases = "__all__"
|
|
117
|
+
|
|
118
|
+
@classmethod
|
|
119
|
+
def setUpTestData(cls): # NOQA:N802
|
|
120
|
+
"""Create test data."""
|
|
121
|
+
super().setUpTestData()
|
|
122
|
+
admin_factories.populate_database()
|
|
123
|
+
|
|
124
|
+
def test_alias_creation(self):
|
|
125
|
+
"""Check alias creation."""
|
|
126
|
+
self.set_global_parameter("user_level_learning", True)
|
|
127
|
+
|
|
128
|
+
# Fake activation because we don't have test data yet for
|
|
129
|
+
# amavis...
|
|
130
|
+
lib.setup_manual_learning_for_mbox(
|
|
131
|
+
admin_models.Mailbox.objects.get(address="user", domain__name="test.com")
|
|
132
|
+
)
|
|
133
|
+
lib.setup_manual_learning_for_mbox(
|
|
134
|
+
admin_models.Mailbox.objects.get(address="admin", domain__name="test.com")
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
url = reverse("v2:alias-list")
|
|
138
|
+
values = {
|
|
139
|
+
"address": "alias1000@test.com",
|
|
140
|
+
"recipients": ["admin@test.com"],
|
|
141
|
+
"enabled": True,
|
|
142
|
+
}
|
|
143
|
+
resp = self.client.post(url, values, format="json")
|
|
144
|
+
self.assertEqual(resp.status_code, 201)
|
|
145
|
+
policy = models.Policy.objects.get(policy_name=values["recipients"][0])
|
|
146
|
+
user = models.Users.objects.get(email=values["address"])
|
|
147
|
+
self.assertEqual(user.policy, policy)
|
|
148
|
+
|
|
149
|
+
values = {
|
|
150
|
+
"address": "user@test.com",
|
|
151
|
+
"recipients": ["admin@test.com"],
|
|
152
|
+
"enabled": True,
|
|
153
|
+
}
|
|
154
|
+
resp = self.client.post(url, values, format="json")
|
|
155
|
+
self.assertEqual(resp.status_code, 201)
|
|
156
|
+
policy = models.Policy.objects.get(policy_name=values["recipients"][0])
|
|
157
|
+
user = models.Users.objects.get(email=values["address"])
|
|
158
|
+
self.assertEqual(user.policy, policy)
|
|
159
|
+
|
|
160
|
+
def test_mailbox_rename(self):
|
|
161
|
+
"""Check rename case."""
|
|
162
|
+
self.set_global_parameter("user_level_learning", True)
|
|
163
|
+
|
|
164
|
+
lib.setup_manual_learning_for_mbox(
|
|
165
|
+
admin_models.Mailbox.objects.get(address="user", domain__name="test.com")
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
user = core_models.User.objects.get(username="user@test.com")
|
|
169
|
+
url = reverse("v2:account-detail", args=[user.pk])
|
|
170
|
+
values = {
|
|
171
|
+
"username": "user2@test.com",
|
|
172
|
+
"role": "SimpleUsers",
|
|
173
|
+
"mailbox": {"use_domain_quota": True},
|
|
174
|
+
}
|
|
175
|
+
resp = self.client.put(url, values, format="json")
|
|
176
|
+
self.assertEqual(resp.status_code, 200)
|
|
177
|
+
self.assertTrue(models.Users.objects.filter(email=values["username"]).exists())
|
|
178
|
+
|
|
179
|
+
def test_learn_alias_spam_as_admin(self):
|
|
180
|
+
"""Check learning spam for an alias address as admin user."""
|
|
181
|
+
user = core_models.User.objects.get(username="admin")
|
|
182
|
+
recipient_db = "user"
|
|
183
|
+
rcpt = "alias@test.com"
|
|
184
|
+
sender = "spam@evil.corp"
|
|
185
|
+
content = factories.SPAM_BODY.format(rcpt=rcpt, sender=sender)
|
|
186
|
+
|
|
187
|
+
saclient = lib.SpamassassinClient(user, recipient_db)
|
|
188
|
+
result = saclient.learn_spam(rcpt, content)
|
|
189
|
+
self.assertTrue(result)
|
|
190
|
+
|
|
191
|
+
def test_delete_catchall_alias(self):
|
|
192
|
+
"""Check that Users record is not deleted."""
|
|
193
|
+
self.set_global_parameter("user_level_learning", True)
|
|
194
|
+
|
|
195
|
+
# Fake activation because we don't have test data yet for
|
|
196
|
+
# amavis...
|
|
197
|
+
lib.setup_manual_learning_for_mbox(
|
|
198
|
+
admin_models.Mailbox.objects.get(address="admin", domain__name="test.com")
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
url = reverse("v2:alias-list")
|
|
202
|
+
values = {
|
|
203
|
+
"address": "@test.com",
|
|
204
|
+
"recipients": ["admin@test.com"],
|
|
205
|
+
"enabled": True,
|
|
206
|
+
}
|
|
207
|
+
resp = self.client.post(url, values, format="json")
|
|
208
|
+
self.assertTrue(resp.status_code, 201)
|
|
209
|
+
|
|
210
|
+
alias = admin_models.Alias.objects.get(address="@test.com")
|
|
211
|
+
url = reverse("v2:alias-detail", args=[alias.pk])
|
|
212
|
+
resp = self.client.delete(url)
|
|
213
|
+
self.assertEqual(resp.status_code, 204)
|
|
214
|
+
self.assertTrue(models.Users.objects.get(email="@test.com"))
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
from django.test import SimpleTestCase
|
|
2
|
+
|
|
3
|
+
from modoboa.lib.tests import ModoTestCase
|
|
4
|
+
from modoboa.amavis.lib import cleanup_email_address, make_query_args
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class MakeQueryArgsTests(ModoTestCase):
|
|
8
|
+
"""Tests for modoboa_amavis.lib.make_query_args()."""
|
|
9
|
+
|
|
10
|
+
def test_simple_email_address_001(self):
|
|
11
|
+
"""Check case insensitive email address without recipient delimiter."""
|
|
12
|
+
self.set_global_parameter("localpart_is_case_sensitive", False)
|
|
13
|
+
self.set_global_parameter("recipient_delimiter", "")
|
|
14
|
+
address = "User+Foo@sub.exAMPLE.COM"
|
|
15
|
+
expected_output = [
|
|
16
|
+
"user+foo@sub.example.com",
|
|
17
|
+
]
|
|
18
|
+
output = make_query_args(address)
|
|
19
|
+
self.assertEqual(output, expected_output)
|
|
20
|
+
|
|
21
|
+
def test_simple_email_address_002(self):
|
|
22
|
+
"""Check case sensitive email address with recipient delimiter."""
|
|
23
|
+
self.set_global_parameter("localpart_is_case_sensitive", True)
|
|
24
|
+
self.set_global_parameter("recipient_delimiter", "+")
|
|
25
|
+
address = "User+Foo@sub.exAMPLE.COM"
|
|
26
|
+
expected_output = [
|
|
27
|
+
"User+Foo@sub.exAMPLE.COM",
|
|
28
|
+
"User+Foo@sub.example.com",
|
|
29
|
+
"User@sub.example.com",
|
|
30
|
+
]
|
|
31
|
+
output = make_query_args(address)
|
|
32
|
+
self.assertEqual(output, expected_output)
|
|
33
|
+
|
|
34
|
+
def test_simple_email_address_003(self):
|
|
35
|
+
"""Check email address with recipient delimiter wildcard."""
|
|
36
|
+
self.set_global_parameter("localpart_is_case_sensitive", False)
|
|
37
|
+
self.set_global_parameter("recipient_delimiter", "+")
|
|
38
|
+
address = "User+Foo@sub.exAMPLE.COM"
|
|
39
|
+
expected_output = [
|
|
40
|
+
"user+foo@sub.example.com",
|
|
41
|
+
"user+.*@sub.example.com",
|
|
42
|
+
"user@sub.example.com",
|
|
43
|
+
]
|
|
44
|
+
output = make_query_args(address, exact_extension=False, wildcard=".*")
|
|
45
|
+
self.assertEqual(output, expected_output)
|
|
46
|
+
|
|
47
|
+
def test_simple_email_address_004(self):
|
|
48
|
+
"""Check domain search."""
|
|
49
|
+
self.set_global_parameter("localpart_is_case_sensitive", False)
|
|
50
|
+
self.set_global_parameter("recipient_delimiter", "+")
|
|
51
|
+
address = "User+Foo@sub.exAMPLE.COM"
|
|
52
|
+
expected_output = [
|
|
53
|
+
"user+foo@sub.example.com",
|
|
54
|
+
"user+.*@sub.example.com",
|
|
55
|
+
"user@sub.example.com",
|
|
56
|
+
"@sub.example.com",
|
|
57
|
+
"@.",
|
|
58
|
+
]
|
|
59
|
+
output = make_query_args(
|
|
60
|
+
address, exact_extension=False, wildcard=".*", domain_search=True
|
|
61
|
+
)
|
|
62
|
+
self.assertEqual(output, expected_output)
|
|
63
|
+
|
|
64
|
+
def test_simple_email_address_idn(self):
|
|
65
|
+
"""Check email address with international domain name."""
|
|
66
|
+
self.set_global_parameter("localpart_is_case_sensitive", False)
|
|
67
|
+
self.set_global_parameter("recipient_delimiter", "")
|
|
68
|
+
address = "Pingüino@Pájaro.Niño.exAMPLE.COM"
|
|
69
|
+
expected_output = [
|
|
70
|
+
"Pingüino@Pájaro.Niño.exAMPLE.COM",
|
|
71
|
+
"pingüino@xn--pjaro-xqa.xn--nio-8ma.example.com",
|
|
72
|
+
]
|
|
73
|
+
output = make_query_args(address)
|
|
74
|
+
self.assertEqual(output, expected_output)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class FixUTF8EncodingTests(SimpleTestCase):
|
|
78
|
+
"""Tests for modoboa_amavis.lib.cleanup_email_address()."""
|
|
79
|
+
|
|
80
|
+
def test_value_with_newline(self):
|
|
81
|
+
value = '"John Smith" <john.smith@example.com>\n'
|
|
82
|
+
expected_output = "John Smith <john.smith@example.com>"
|
|
83
|
+
output = cleanup_email_address(value)
|
|
84
|
+
self.assertEqual(output, expected_output)
|
|
85
|
+
|
|
86
|
+
def test_no_name(self):
|
|
87
|
+
value = "<john.smith@example.com>"
|
|
88
|
+
expected_output = "john.smith@example.com"
|
|
89
|
+
output = cleanup_email_address(value)
|
|
90
|
+
self.assertEqual(output, expected_output)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""Management commands tests."""
|
|
2
|
+
|
|
3
|
+
from dateutil.relativedelta import relativedelta
|
|
4
|
+
|
|
5
|
+
from django.core.management import call_command
|
|
6
|
+
from django.utils import timezone
|
|
7
|
+
|
|
8
|
+
from modoboa.lib.tests import ModoTestCase
|
|
9
|
+
from .. import factories, models
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ManagementCommandTestCase(ModoTestCase):
|
|
13
|
+
"""Management commands tests."""
|
|
14
|
+
|
|
15
|
+
databases = "__all__"
|
|
16
|
+
|
|
17
|
+
def test_qcleanup(self):
|
|
18
|
+
"""Test qcleanup command."""
|
|
19
|
+
factories.create_spam("user@test.com", rs="D")
|
|
20
|
+
call_command("qcleanup")
|
|
21
|
+
self.assertEqual(models.Quarantine.objects.count(), 0)
|
|
22
|
+
self.assertEqual(models.Msgs.objects.count(), 0)
|
|
23
|
+
self.assertEqual(models.Maddr.objects.count(), 0)
|
|
24
|
+
self.assertEqual(models.Msgrcpt.objects.count(), 0)
|
|
25
|
+
|
|
26
|
+
factories.create_spam("user@test.com", rs="D")
|
|
27
|
+
msgrcpt = factories.create_spam("user@test.com", rs="R")
|
|
28
|
+
call_command("qcleanup")
|
|
29
|
+
# Should not raise anything
|
|
30
|
+
msgrcpt.refresh_from_db()
|
|
31
|
+
|
|
32
|
+
self.set_global_parameter("released_msgs_cleanup", True)
|
|
33
|
+
call_command("qcleanup")
|
|
34
|
+
with self.assertRaises(models.Msgrcpt.DoesNotExist):
|
|
35
|
+
msgrcpt.refresh_from_db()
|
|
36
|
+
|
|
37
|
+
msgrcpt = factories.create_spam("user@test.com")
|
|
38
|
+
msgrcpt.mail.time_num = int(
|
|
39
|
+
(timezone.now() - relativedelta(days=40)).strftime("%s")
|
|
40
|
+
)
|
|
41
|
+
msgrcpt.mail.save(update_fields=["time_num"])
|
|
42
|
+
self.set_global_parameter("max_messages_age", 30)
|
|
43
|
+
call_command("qcleanup")
|
|
44
|
+
with self.assertRaises(models.Msgrcpt.DoesNotExist):
|
|
45
|
+
msgrcpt.refresh_from_db()
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"""Tests for sql_email."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
from django.test import TestCase
|
|
6
|
+
from django.utils.encoding import smart_str
|
|
7
|
+
|
|
8
|
+
from ..sql_email import SQLemail
|
|
9
|
+
|
|
10
|
+
SAMPLES_DIR = os.path.realpath(
|
|
11
|
+
os.path.join(os.path.dirname(__file__), "sample_messages")
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class EmailTestImplementation(SQLemail):
|
|
16
|
+
|
|
17
|
+
def _fetch_message(self):
|
|
18
|
+
message_path = os.path.join(SAMPLES_DIR, f"{self.mailid}-input.txt")
|
|
19
|
+
assert os.path.isfile(message_path), f"{message_path} does not exist."
|
|
20
|
+
|
|
21
|
+
with open(message_path, "rb") as fp:
|
|
22
|
+
mail_text = fp.read()
|
|
23
|
+
|
|
24
|
+
return mail_text
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class EmailTests(TestCase):
|
|
28
|
+
"""Tests for modoboa_amavis.sql_email.SQLEmail
|
|
29
|
+
|
|
30
|
+
When writing new sample messages use the following naming convention for
|
|
31
|
+
the sample files stored in sample_messages:
|
|
32
|
+
|
|
33
|
+
input: {message_id}-input.txt
|
|
34
|
+
output: {message_id}-output-{dformat}_{no,}links.txt
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
def _get_expected_output(self, message_id, **kwargs):
|
|
38
|
+
ext = kwargs.get("dformat", "plain")
|
|
39
|
+
ext += "_links" if "links" in kwargs and kwargs["links"] else "_nolinks"
|
|
40
|
+
message_path = os.path.join(SAMPLES_DIR, f"{message_id}-output-{ext}.txt")
|
|
41
|
+
assert os.path.isfile(message_path), f"{message_path} does not exist."
|
|
42
|
+
|
|
43
|
+
with open(message_path, "rb") as fp:
|
|
44
|
+
# output should always be unicode (py2) or str (py3)
|
|
45
|
+
mail_text = smart_str(fp.read())
|
|
46
|
+
|
|
47
|
+
return mail_text
|
|
48
|
+
|
|
49
|
+
def _test_email(self, message_id, **kwargs):
|
|
50
|
+
"""Boiler plate code for testing e-mails."""
|
|
51
|
+
expected_output = self._get_expected_output(message_id, **kwargs)
|
|
52
|
+
output = EmailTestImplementation(message_id, **kwargs).body
|
|
53
|
+
self.assertEqual(output, expected_output)
|
|
54
|
+
|
|
55
|
+
def test_amavis_aleart_header(self):
|
|
56
|
+
email = EmailTestImplementation("quarantined")
|
|
57
|
+
self.assertEqual(email.qtype, "BAD HEADER SECTION")
|
|
58
|
+
self.assertEqual(
|
|
59
|
+
email.qreason,
|
|
60
|
+
"Non-encoded non-ASCII data (and not UTF-8) (char 85 "
|
|
61
|
+
"hex): Subject: I think I saw you in my dreams\\x{85}",
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
def test_email_multipart_with_no_text(self):
|
|
65
|
+
"""for a multipart message without a text/plain part convert the
|
|
66
|
+
text/html to text/plain"""
|
|
67
|
+
self._test_email("quarantined")
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from django.test import SimpleTestCase
|
|
2
|
+
|
|
3
|
+
from modoboa.amavis.utils import fix_utf8_encoding
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class FixUTF8EncodingTests(SimpleTestCase):
|
|
7
|
+
"""Tests for modoboa_amavis.utils.fix_utf8_encoding()."""
|
|
8
|
+
|
|
9
|
+
def test_4_byte_unicode(self):
|
|
10
|
+
value = "\xf0\x9f\x99\x88"
|
|
11
|
+
expected_output = "\U0001f648" # == See No Evil Moneky
|
|
12
|
+
output = fix_utf8_encoding(value)
|
|
13
|
+
self.assertEqual(output, expected_output)
|
|
14
|
+
|
|
15
|
+
def test_truncated_4_byte_unicode(self):
|
|
16
|
+
value = "\xf0\x9f\x99"
|
|
17
|
+
expected_output = "\xf0\x9f\x99"
|
|
18
|
+
output = fix_utf8_encoding(value)
|
|
19
|
+
self.assertEqual(output, expected_output)
|