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.
Files changed (318) hide show
  1. modoboa/admin/api/v2/serializers.py +14 -0
  2. modoboa/amavis/__init__.py +3 -0
  3. modoboa/amavis/app_settings.py +276 -0
  4. modoboa/amavis/apps.py +18 -0
  5. modoboa/amavis/checks/__init__.py +2 -0
  6. modoboa/amavis/checks/settings_checks.py +59 -0
  7. modoboa/amavis/dbrouter.py +35 -0
  8. modoboa/amavis/factories.py +164 -0
  9. modoboa/amavis/handlers.py +146 -0
  10. modoboa/amavis/lib.py +381 -0
  11. modoboa/amavis/management/__init__.py +0 -0
  12. modoboa/amavis/management/commands/__init__.py +0 -0
  13. modoboa/amavis/management/commands/amnotify.py +99 -0
  14. modoboa/amavis/management/commands/qcleanup.py +84 -0
  15. modoboa/amavis/migrations/0001_initial.py +340 -0
  16. modoboa/amavis/migrations/__init__.py +0 -0
  17. modoboa/amavis/models.py +226 -0
  18. modoboa/amavis/serializers.py +139 -0
  19. modoboa/amavis/sql_connector.py +240 -0
  20. modoboa/amavis/sql_email.py +66 -0
  21. modoboa/amavis/tasks.py +33 -0
  22. modoboa/amavis/templates/amavis/notifications/pending_requests.html +16 -0
  23. modoboa/amavis/tests/__init__.py +0 -0
  24. modoboa/amavis/tests/sa-learn +3 -0
  25. modoboa/amavis/tests/sample_messages/quarantined-input.txt +80 -0
  26. modoboa/amavis/tests/sample_messages/quarantined-output-plain_nolinks.txt +17 -0
  27. modoboa/amavis/tests/spamc +3 -0
  28. modoboa/amavis/tests/test_checks.py +25 -0
  29. modoboa/amavis/tests/test_handlers.py +214 -0
  30. modoboa/amavis/tests/test_lib.py +90 -0
  31. modoboa/amavis/tests/test_management_commands.py +45 -0
  32. modoboa/amavis/tests/test_sql_email.py +67 -0
  33. modoboa/amavis/tests/test_utils.py +19 -0
  34. modoboa/amavis/tests/test_viewsets.py +319 -0
  35. modoboa/amavis/urls.py +11 -0
  36. modoboa/amavis/utils.py +105 -0
  37. modoboa/amavis/viewsets.py +265 -0
  38. modoboa/core/api/v1/serializers.py +7 -5
  39. modoboa/core/api/v2/serializers.py +4 -2
  40. modoboa/core/api/v2/tests.py +16 -4
  41. modoboa/core/api/v2/urls.py +5 -5
  42. modoboa/core/api/v2/views.py +6 -2
  43. modoboa/core/api/v2/viewsets.py +24 -3
  44. modoboa/core/commands/deploy.py +3 -0
  45. modoboa/core/commands/templates/settings.py.tpl +12 -11
  46. modoboa/core/handlers.py +6 -2
  47. modoboa/core/management/commands/add_allowed_hosts.py +33 -0
  48. modoboa/core/management/commands/load_initial_data.py +10 -0
  49. modoboa/core/migrations/0025_rename_user_email_is_active_core_user_email_c0c03f_idx.py +23 -5
  50. modoboa/core/tests/test_core.py +24 -0
  51. modoboa/core/utils.py +3 -0
  52. modoboa/frontend_dist/assets/AccountAliasForm-C0oHHyZL.js +1 -0
  53. modoboa/frontend_dist/assets/AccountEditView-lgSJ2Se8.js +1 -0
  54. modoboa/frontend_dist/assets/AccountLayout-U386K8zy.js +1 -0
  55. modoboa/frontend_dist/assets/AccountPasswordSubForm-YsaE_cDx.js +1 -0
  56. modoboa/frontend_dist/assets/AccountView-1jfKFDwb.js +1 -0
  57. modoboa/frontend_dist/assets/AddressBook-CwN64Zls.js +1 -0
  58. modoboa/frontend_dist/assets/AdminLayout-Cxm1lggg.js +1 -0
  59. modoboa/frontend_dist/assets/AlarmsView-9yKGbmkC.css +1 -0
  60. modoboa/frontend_dist/assets/AlarmsView-Bcjsicac.js +1 -0
  61. modoboa/frontend_dist/assets/AliasEditView-k3rVt1tG.js +1 -0
  62. modoboa/frontend_dist/assets/{AliasRecipientForm-IOae6sjF.js → AliasRecipientForm-IehUzKok.js} +1 -1
  63. modoboa/frontend_dist/assets/AliasView-DMzA10eD.js +1 -0
  64. modoboa/frontend_dist/assets/AuditTrailView-5dnGX5El.js +1 -0
  65. modoboa/frontend_dist/assets/CalendarView-DZONeDA9.js +1 -0
  66. modoboa/frontend_dist/assets/{ChoiceField-DJ_c78Cm.js → ChoiceField-DnwXRkht.js} +1 -1
  67. modoboa/frontend_dist/assets/ComposeEmailForm-kghmfNuE.js +1 -0
  68. modoboa/frontend_dist/assets/ComposeEmailView-DLv3wk1k.js +1 -0
  69. modoboa/frontend_dist/assets/ConfirmDialog-CcPrCKuI.js +1 -0
  70. modoboa/frontend_dist/assets/{ConnectedLayout-Dvwmicnc.css → ConnectedLayout-Bxh21hcH.css} +1 -1
  71. modoboa/frontend_dist/assets/ConnectedLayout-CWlBK7Hf.js +1 -0
  72. modoboa/frontend_dist/assets/CreationForm-CW4lxnPg.js +1 -0
  73. modoboa/frontend_dist/assets/DashboardView-DXVZMbMo.js +1 -0
  74. modoboa/frontend_dist/assets/DomainAdminList-C3jcDDc3.js +1 -0
  75. modoboa/frontend_dist/assets/DomainEditView-ph8AaElX.js +1 -0
  76. modoboa/frontend_dist/assets/{DomainTransportForm-C2xo0Yd7.js → DomainTransportForm-NCz6Bl-h.js} +1 -1
  77. modoboa/frontend_dist/assets/DomainView-BgMSSuU-.js +5 -0
  78. modoboa/frontend_dist/assets/{DomainView-BDKoBFYr.css → DomainView-CCLYXPHx.css} +1 -1
  79. modoboa/frontend_dist/assets/DomainsView-CEEU9btK.js +1 -0
  80. modoboa/frontend_dist/assets/DomainsView-DZ-ss9bI.css +1 -0
  81. modoboa/frontend_dist/assets/EmailField-DeqDPm5j.js +1 -0
  82. modoboa/frontend_dist/assets/EmailView-DczVhVO0.js +1 -0
  83. modoboa/frontend_dist/assets/EmptyLayout-BXgcfMLH.js +1 -0
  84. modoboa/frontend_dist/assets/FiltersView-nJj_gSCx.js +1 -0
  85. modoboa/frontend_dist/assets/ForwardEmailView-Bgv3JQb6.js +1 -0
  86. modoboa/frontend_dist/assets/{HtmlEditor-CJ9umKeO.js → HtmlEditor-BWRdelVw.js} +1 -1
  87. modoboa/frontend_dist/assets/{IdentitiesView-0ziuQ5s-.css → IdentitiesView-DPrrRMS5.css} +1 -1
  88. modoboa/frontend_dist/assets/IdentitiesView-Dld9IloZ.js +1 -0
  89. modoboa/frontend_dist/assets/InformationView-BBWKSX8D.js +1 -0
  90. modoboa/frontend_dist/assets/InformationView-C9vvvQhJ.css +1 -0
  91. modoboa/frontend_dist/assets/{LoadingData-CYwX3Jpn.js → LoadingData-G57nJ_JV.js} +1 -1
  92. modoboa/frontend_dist/assets/{LoginCallbackView-E01qkKn0.js → LoginCallbackView-DjyE2SG_.js} +1 -1
  93. modoboa/frontend_dist/assets/{LoginView-Cy4uFV9h.js → LoginView-CqCCXYLo.js} +1 -1
  94. modoboa/frontend_dist/assets/{MailboxView-B-aI4XBq.css → MailboxView-CfStlWhk.css} +1 -1
  95. modoboa/frontend_dist/assets/MailboxView-DRrs9eLO.js +1 -0
  96. modoboa/frontend_dist/assets/MenuItems-BqIZW5av.js +1 -0
  97. modoboa/frontend_dist/assets/MessageView-D_6tx_gd.js +1 -0
  98. modoboa/frontend_dist/assets/MessagesView-BH7JIR03.js +1 -0
  99. modoboa/frontend_dist/assets/MigrationsView-Cv_So9T-.js +1 -0
  100. modoboa/frontend_dist/assets/{ParametersForm-BZM0QSvg.js → ParametersForm-3qXttTuQ.js} +1 -1
  101. modoboa/frontend_dist/assets/ParametersView-3Ns04cpQ.js +1 -0
  102. modoboa/frontend_dist/assets/ParametersView-B5B5Dt6K.js +1 -0
  103. modoboa/frontend_dist/assets/ProviderEditView-zh7CY832.js +1 -0
  104. modoboa/frontend_dist/assets/ProviderGeneralForm-BQU7t3ma.js +1 -0
  105. modoboa/frontend_dist/assets/ProvidersView-CoF_ZkZA.js +1 -0
  106. modoboa/frontend_dist/assets/QuarantineLayout-CYBsrbJM.js +1 -0
  107. modoboa/frontend_dist/assets/QuarantineView-D4gOE4EQ.css +1 -0
  108. modoboa/frontend_dist/assets/QuarantineView-DNvpoycb.js +1 -0
  109. modoboa/frontend_dist/assets/ReplyEmailView-D1XTcglu.js +1 -0
  110. modoboa/frontend_dist/assets/ResourcesForm-BW8rUGgZ.js +1 -0
  111. modoboa/frontend_dist/assets/SelfServiceLayout-DfDHiYeX.js +1 -0
  112. modoboa/frontend_dist/assets/{SettingsView-BxLJBFY0.js → SettingsView-gQiJ2NVb.js} +2 -2
  113. modoboa/frontend_dist/assets/StatisticsView-DYalet_q.js +1 -0
  114. modoboa/frontend_dist/assets/TimeSerieChart-BZ2htbFk.js +1 -0
  115. modoboa/frontend_dist/assets/UserLayout-zUtHi-z-.js +1 -0
  116. modoboa/frontend_dist/assets/VAlert-4r6LxKtg.js +1 -0
  117. modoboa/frontend_dist/assets/VApp-CX_C7AUN.js +1 -0
  118. modoboa/frontend_dist/assets/VAutocomplete-DNKmBvyZ.js +1 -0
  119. modoboa/frontend_dist/assets/VAvatar-DbuoZWmf.js +1 -0
  120. modoboa/frontend_dist/assets/VBadge-BQrRJ9S0.css +1 -0
  121. modoboa/frontend_dist/assets/VBadge-Bv2nvUmC.js +1 -0
  122. modoboa/frontend_dist/assets/VCard-DzjUT5OP.js +1 -0
  123. modoboa/frontend_dist/assets/VCheckbox-dr7UFjl4.js +1 -0
  124. modoboa/frontend_dist/assets/{VCheckboxBtn-Dt810gWf.js → VCheckboxBtn-CpFdBnTv.js} +1 -1
  125. modoboa/frontend_dist/assets/VChip-CaQvfmkw.js +1 -0
  126. modoboa/frontend_dist/assets/VColorPicker-ByGpCW5O.js +1 -0
  127. modoboa/frontend_dist/assets/{VContainer-DvTbsotR.js → VContainer-74Dnn8Ux.js} +1 -1
  128. modoboa/frontend_dist/assets/VDataTable-CL7yHvG7.js +1 -0
  129. modoboa/frontend_dist/assets/VDataTableServer-BqvNcIdw.js +1 -0
  130. modoboa/frontend_dist/assets/VDataTableVirtual--KsOP8i6.js +1 -0
  131. modoboa/frontend_dist/assets/{VDialog-Bk6EWNhz.js → VDialog-DmTGCGR0.js} +1 -1
  132. modoboa/frontend_dist/assets/VExpansionPanels-B7sSTCwd.js +1 -0
  133. modoboa/frontend_dist/assets/VFileInput-SULIc6F3.js +1 -0
  134. modoboa/frontend_dist/assets/VForm-DsRLc-sa.js +1 -0
  135. modoboa/frontend_dist/assets/VInput-CcxkaOXT.css +1 -0
  136. modoboa/frontend_dist/assets/VInput-DVKUObZe.js +1 -0
  137. modoboa/frontend_dist/assets/VMenu-nv0XOgg0.js +1 -0
  138. modoboa/frontend_dist/assets/VPicker-DnDSWJHJ.js +1 -0
  139. modoboa/frontend_dist/assets/VProgressCircular-qK6p5X_Y.js +1 -0
  140. modoboa/frontend_dist/assets/VRadioGroup-CbiPLy0t.js +1 -0
  141. modoboa/frontend_dist/assets/{VRow-BF35mT1S.js → VRow-DJ0NB63-.js} +1 -1
  142. modoboa/frontend_dist/assets/VSelect-CxCFMHyF.js +1 -0
  143. modoboa/frontend_dist/assets/VSelectionControl-C-6A4us5.js +1 -0
  144. modoboa/frontend_dist/assets/VSheet-DI6SxLnG.js +1 -0
  145. modoboa/frontend_dist/assets/VSpacer-CoJVmx8k.js +1 -0
  146. modoboa/frontend_dist/assets/VSwitch-DPnjPQuU.js +1 -0
  147. modoboa/frontend_dist/assets/VTable-ldTxgQPW.js +1 -0
  148. modoboa/frontend_dist/assets/VTabs-aS8WSL9I.js +1 -0
  149. modoboa/frontend_dist/assets/VTextField-BzBVKKob.css +1 -0
  150. modoboa/frontend_dist/assets/VTextField-DKbr4H5w.js +1 -0
  151. modoboa/frontend_dist/assets/VTextarea-BttkFsM4.js +1 -0
  152. modoboa/frontend_dist/assets/VToolbar-BxX3W2kR.js +1 -0
  153. modoboa/frontend_dist/assets/VWindowItem-Cvqvdegd.js +1 -0
  154. modoboa/frontend_dist/assets/WebmailLayout-BT2k6U7q.js +1 -0
  155. modoboa/frontend_dist/assets/accounts-CC2F0a0c.js +1 -0
  156. modoboa/frontend_dist/assets/{admin-o-HRGnmT.js → admin-CHCHFGI6.js} +1 -1
  157. modoboa/frontend_dist/assets/{aliases-DDVeehyg.js → aliases-C9bUD4Ws.js} +1 -1
  158. modoboa/frontend_dist/assets/amavis-BhzV4rgf.js +1 -0
  159. modoboa/frontend_dist/assets/amavis-DCVJxuui.js +1 -0
  160. modoboa/frontend_dist/assets/{contacts-C84DY-Q1.js → contacts-Dxz6eWpf.js} +1 -1
  161. modoboa/frontend_dist/assets/{domains-Bgn4ixHL.js → domains-C2cornvL.js} +1 -1
  162. modoboa/frontend_dist/assets/{domains.store-DTE-V7Y1.js → domains.store-BLKRipG8.js} +1 -1
  163. modoboa/frontend_dist/assets/{filter-CnffiQAW.js → filter-rmxrcjKk.js} +1 -1
  164. modoboa/frontend_dist/assets/forwardRefs-CpzzjgpX.js +1 -0
  165. modoboa/frontend_dist/assets/global.store-DndbMXYb.js +1 -0
  166. modoboa/frontend_dist/assets/{importExport-BlQYb0NO.js → importExport-C3uqrcok.js} +1 -1
  167. modoboa/frontend_dist/assets/index-LhNzkzAh.js +984 -0
  168. modoboa/frontend_dist/assets/layout-DbjDe3Wl.js +1 -0
  169. modoboa/frontend_dist/assets/{layout.store-DkjrAoXt.js → layout.store-Vq5mvIp7.js} +1 -1
  170. modoboa/frontend_dist/assets/{logos-q8SEyAa4.js → logos-Bvcy0usu.js} +1 -1
  171. modoboa/frontend_dist/assets/{logs-B7IJ7LBa.js → logs-BuItINky.js} +1 -1
  172. modoboa/frontend_dist/assets/{parameters-A6iBEYQq.js → parameters-C8IYEP7q.js} +1 -1
  173. modoboa/frontend_dist/assets/{parameters.store-BiXS4_6w.js → parameters.store-1cwSP2JP.js} +1 -1
  174. modoboa/frontend_dist/assets/permissions-DQjAcO9S.js +1 -0
  175. modoboa/frontend_dist/assets/{ssrBoot-AzTdjPjk.js → ssrBoot-BxIQ9ccA.js} +1 -1
  176. modoboa/frontend_dist/assets/{tag-BnSYRTcD.js → tag-3cfI1_f7.js} +1 -1
  177. modoboa/frontend_dist/assets/transports-D4Jk4-AP.js +1 -0
  178. modoboa/frontend_dist/assets/{webmail-CSH_3l6R.js → webmail-B2IUjaxM.js} +1 -1
  179. modoboa/frontend_dist/index.html +1 -1
  180. modoboa/lib/email_utils.py +2 -2
  181. modoboa/lib/permissions.py +7 -0
  182. modoboa/lib/test_runners.py +29 -0
  183. modoboa/lib/tests/__init__.py +5 -1
  184. modoboa/locale/br/LC_MESSAGES/django.po +87 -75
  185. modoboa/locale/cs/LC_MESSAGES/django.po +82 -74
  186. modoboa/locale/cs_CZ/LC_MESSAGES/django.po +145 -121
  187. modoboa/locale/de/LC_MESSAGES/django.mo +0 -0
  188. modoboa/locale/de/LC_MESSAGES/django.po +339 -651
  189. modoboa/locale/de_DE/LC_MESSAGES/django.po +87 -75
  190. modoboa/locale/el_GR/LC_MESSAGES/django.po +160 -135
  191. modoboa/locale/en/LC_MESSAGES/django.po +82 -74
  192. modoboa/locale/es/LC_MESSAGES/django.po +158 -131
  193. modoboa/locale/es_MX/LC_MESSAGES/django.po +82 -74
  194. modoboa/locale/fi/LC_MESSAGES/django.po +87 -75
  195. modoboa/locale/fr/LC_MESSAGES/django.mo +0 -0
  196. modoboa/locale/fr/LC_MESSAGES/django.po +469 -201
  197. modoboa/locale/hu/LC_MESSAGES/django.po +82 -74
  198. modoboa/locale/it/LC_MESSAGES/django.mo +0 -0
  199. modoboa/locale/it/LC_MESSAGES/django.po +148 -122
  200. modoboa/locale/ja_JP/LC_MESSAGES/django.mo +0 -0
  201. modoboa/locale/ja_JP/LC_MESSAGES/django.po +80 -72
  202. modoboa/locale/ka/LC_MESSAGES/django.po +82 -74
  203. modoboa/locale/nl_NL/LC_MESSAGES/django.po +160 -132
  204. modoboa/locale/no/LC_MESSAGES/django.po +82 -74
  205. modoboa/locale/pl_PL/LC_MESSAGES/django.mo +0 -0
  206. modoboa/locale/pl_PL/LC_MESSAGES/django.po +172 -149
  207. modoboa/locale/pt_BR/LC_MESSAGES/django.mo +0 -0
  208. modoboa/locale/pt_BR/LC_MESSAGES/django.po +172 -144
  209. modoboa/locale/pt_PT/LC_MESSAGES/django.po +135 -112
  210. modoboa/locale/ro_RO/LC_MESSAGES/django.po +87 -75
  211. modoboa/locale/ru/LC_MESSAGES/django.po +142 -118
  212. modoboa/locale/si/LC_MESSAGES/django.po +82 -74
  213. modoboa/locale/sk/LC_MESSAGES/django.po +82 -74
  214. modoboa/locale/sk_SK/LC_MESSAGES/django.po +84 -76
  215. modoboa/locale/sl_SI/LC_MESSAGES/django.po +90 -76
  216. modoboa/locale/sv/LC_MESSAGES/django.mo +0 -0
  217. modoboa/locale/sv/LC_MESSAGES/django.po +172 -139
  218. modoboa/locale/tr/LC_MESSAGES/django.po +87 -75
  219. modoboa/locale/tr_TR/LC_MESSAGES/django.po +84 -74
  220. modoboa/locale/uk/LC_MESSAGES/django.po +82 -74
  221. modoboa/locale/zh/LC_MESSAGES/django.po +82 -74
  222. modoboa/locale/zh_CN/LC_MESSAGES/django.po +82 -74
  223. modoboa/locale/zh_TW/LC_MESSAGES/django.po +87 -75
  224. modoboa/parameters/api/v2/tests.py +2 -2
  225. modoboa/parameters/api/v2/viewsets.py +2 -0
  226. modoboa/policyd/tests.py +2 -0
  227. modoboa/urls_api_v2.py +6 -0
  228. modoboa/webmail/lib/imaputils.py +2 -2
  229. {modoboa-2.4.11.dist-info → modoboa-2.5.1.dist-info}/METADATA +6 -4
  230. {modoboa-2.4.11.dist-info → modoboa-2.5.1.dist-info}/RECORD +235 -185
  231. modoboa/frontend_dist/assets/AccountAliasForm-BV6KvTu6.js +0 -1
  232. modoboa/frontend_dist/assets/AccountEditView-DDOFyfBD.js +0 -1
  233. modoboa/frontend_dist/assets/AccountLayout-rX51xgxT.js +0 -1
  234. modoboa/frontend_dist/assets/AccountPasswordSubForm-D9S6LaeH.js +0 -1
  235. modoboa/frontend_dist/assets/AccountView-cmvaZNq3.js +0 -1
  236. modoboa/frontend_dist/assets/AddressBook-DCJxL8SU.js +0 -1
  237. modoboa/frontend_dist/assets/AdminLayout-r0wfG2lO.js +0 -1
  238. modoboa/frontend_dist/assets/AlarmsView-Bheey-gp.css +0 -1
  239. modoboa/frontend_dist/assets/AlarmsView-C0bqC4PA.js +0 -1
  240. modoboa/frontend_dist/assets/AliasEditView-DVoWoCGY.js +0 -1
  241. modoboa/frontend_dist/assets/AliasView-DrONZXOh.js +0 -1
  242. modoboa/frontend_dist/assets/AuditTrailView-OTkoZaMU.js +0 -1
  243. modoboa/frontend_dist/assets/CalendarView-CqF4_Ui9.js +0 -1
  244. modoboa/frontend_dist/assets/ComposeEmailForm-DO5_GB3e.js +0 -1
  245. modoboa/frontend_dist/assets/ComposeEmailView-A91HCBsN.js +0 -1
  246. modoboa/frontend_dist/assets/ConfirmDialog-BBcgdAnO.js +0 -1
  247. modoboa/frontend_dist/assets/ConnectedLayout-1oRW-Rql.js +0 -1
  248. modoboa/frontend_dist/assets/CreationForm-71YJbjsA.js +0 -1
  249. modoboa/frontend_dist/assets/DashboardView-CdLpSfUl.js +0 -1
  250. modoboa/frontend_dist/assets/DomainAdminList-BjC4KsqI.js +0 -1
  251. modoboa/frontend_dist/assets/DomainEditView-CQjKwYxl.js +0 -1
  252. modoboa/frontend_dist/assets/DomainView-BhhuZI_N.js +0 -5
  253. modoboa/frontend_dist/assets/DomainsView-Cft4BP8Z.js +0 -1
  254. modoboa/frontend_dist/assets/DomainsView-DasJ0NdZ.css +0 -1
  255. modoboa/frontend_dist/assets/EmailField-C8umy0EU.js +0 -1
  256. modoboa/frontend_dist/assets/EmailView-ki7uEQPD.js +0 -1
  257. modoboa/frontend_dist/assets/EmptyLayout-DaA1XH9n.js +0 -1
  258. modoboa/frontend_dist/assets/FiltersView-FYFZxG4B.js +0 -1
  259. modoboa/frontend_dist/assets/ForwardEmailView-cUbnSYCF.js +0 -1
  260. modoboa/frontend_dist/assets/IdentitiesView-njNo8N5n.js +0 -1
  261. modoboa/frontend_dist/assets/InformationView-D1h38POt.js +0 -1
  262. modoboa/frontend_dist/assets/InformationView-U5Ww-Sx1.css +0 -1
  263. modoboa/frontend_dist/assets/MailboxView-IlrLWm_H.js +0 -1
  264. modoboa/frontend_dist/assets/MenuItems-BAtHWzAE.js +0 -1
  265. modoboa/frontend_dist/assets/MessagesView-OSpjixFq.js +0 -1
  266. modoboa/frontend_dist/assets/MigrationsView-DKNOsVzF.js +0 -1
  267. modoboa/frontend_dist/assets/ParametersView-C4bXASiq.js +0 -1
  268. modoboa/frontend_dist/assets/ParametersView-CYXgNmc1.js +0 -1
  269. modoboa/frontend_dist/assets/ProviderEditView-CyxCWTST.js +0 -1
  270. modoboa/frontend_dist/assets/ProviderGeneralForm-BYPjNHqB.js +0 -1
  271. modoboa/frontend_dist/assets/ProvidersView-CxrMkRyk.js +0 -1
  272. modoboa/frontend_dist/assets/ReplyEmailView-Dkw9-N26.js +0 -1
  273. modoboa/frontend_dist/assets/ResourcesForm-CuUvrOdY.js +0 -1
  274. modoboa/frontend_dist/assets/StatisticsView-BN7QsZMT.js +0 -1
  275. modoboa/frontend_dist/assets/TimeSerieChart-BMN8BeFZ.js +0 -1
  276. modoboa/frontend_dist/assets/UserLayout-B6-JQg4F.js +0 -1
  277. modoboa/frontend_dist/assets/VAlert-DIQTrRif.js +0 -1
  278. modoboa/frontend_dist/assets/VApp-CpkYA7js.js +0 -1
  279. modoboa/frontend_dist/assets/VAutocomplete-C4IpXyl8.js +0 -1
  280. modoboa/frontend_dist/assets/VAvatar-Lpb-Dion.js +0 -1
  281. modoboa/frontend_dist/assets/VCard-er_isjE_.js +0 -1
  282. modoboa/frontend_dist/assets/VCheckbox-D-u8JXP1.js +0 -1
  283. modoboa/frontend_dist/assets/VChip-B4iSpj8_.js +0 -1
  284. modoboa/frontend_dist/assets/VColorPicker-BAjGDsXv.js +0 -1
  285. modoboa/frontend_dist/assets/VDataTable-4JRjbtgF.js +0 -1
  286. modoboa/frontend_dist/assets/VDataTableServer-tIDT1m3-.js +0 -1
  287. modoboa/frontend_dist/assets/VDataTableVirtual-BlnO18u_.js +0 -1
  288. modoboa/frontend_dist/assets/VExpansionPanels-CwGtXDhr.js +0 -1
  289. modoboa/frontend_dist/assets/VFileInput-D1_7ZkO_.js +0 -1
  290. modoboa/frontend_dist/assets/VForm-DAkW4nfy.js +0 -1
  291. modoboa/frontend_dist/assets/VMenu-BPFJwj2f.js +0 -1
  292. modoboa/frontend_dist/assets/VPicker-CfT82M8N.js +0 -1
  293. modoboa/frontend_dist/assets/VProgressCircular-w75-3ogi.js +0 -1
  294. modoboa/frontend_dist/assets/VRadioGroup-0j6DNC_k.js +0 -1
  295. modoboa/frontend_dist/assets/VSelect-Cs4ARbAS.js +0 -1
  296. modoboa/frontend_dist/assets/VSelectionControl-Dg-XyRRS.js +0 -1
  297. modoboa/frontend_dist/assets/VSheet-Btq_Mu4s.js +0 -1
  298. modoboa/frontend_dist/assets/VSpacer-C7xukQmu.js +0 -1
  299. modoboa/frontend_dist/assets/VSwitch-Cs1NQrmk.js +0 -1
  300. modoboa/frontend_dist/assets/VTable-CNz2SGk4.js +0 -1
  301. modoboa/frontend_dist/assets/VTabs-B1fyVn4M.js +0 -1
  302. modoboa/frontend_dist/assets/VTextField-BdyvgvkG.js +0 -1
  303. modoboa/frontend_dist/assets/VTextField-C-J20yj_.css +0 -1
  304. modoboa/frontend_dist/assets/VTextarea-DnOMpe0Q.js +0 -1
  305. modoboa/frontend_dist/assets/VToolbar-BiCiBxBJ.js +0 -1
  306. modoboa/frontend_dist/assets/VWindowItem-ChWm_kz3.js +0 -1
  307. modoboa/frontend_dist/assets/WebmailLayout-o4uEkp9e.js +0 -1
  308. modoboa/frontend_dist/assets/forwardRefs-Dvjn_Xq4.js +0 -1
  309. modoboa/frontend_dist/assets/global.store-BaiD63EN.js +0 -1
  310. modoboa/frontend_dist/assets/index-I1VDlN4g.js +0 -984
  311. modoboa/frontend_dist/assets/layout-D8ZJPiJ_.js +0 -1
  312. modoboa/frontend_dist/assets/permissions-CITHLHVg.js +0 -1
  313. modoboa/frontend_dist/assets/transports-Dz7c6kIy.js +0 -1
  314. {modoboa-2.4.11.data → modoboa-2.5.1.data}/scripts/modoboa-admin.py +0 -0
  315. {modoboa-2.4.11.dist-info → modoboa-2.5.1.dist-info}/WHEEL +0 -0
  316. {modoboa-2.4.11.dist-info → modoboa-2.5.1.dist-info}/entry_points.txt +0 -0
  317. {modoboa-2.4.11.dist-info → modoboa-2.5.1.dist-info}/licenses/LICENSE +0 -0
  318. {modoboa-2.4.11.dist-info → modoboa-2.5.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,139 @@
1
+ """Amavis serializers."""
2
+
3
+ from django.utils.translation import gettext as _
4
+
5
+ from rest_framework import serializers
6
+
7
+ from modoboa.amavis import models
8
+
9
+
10
+ class GlobalParametersSerializer(serializers.Serializer):
11
+
12
+ localpart_is_case_sensitive = serializers.BooleanField(default=False)
13
+ recipient_delimiter = serializers.CharField(default="", allow_blank=True)
14
+ max_messages_age = serializers.IntegerField(default=14)
15
+ released_msgs_cleanup = serializers.BooleanField(default=False)
16
+ am_pdp_mode = serializers.ChoiceField(
17
+ choices=[("inet", "inet"), ("unix", "unix")],
18
+ default="unix",
19
+ )
20
+ am_pdp_host = serializers.CharField(default="localhost", required=False)
21
+ am_pdp_port = serializers.IntegerField(default=9998, required=False)
22
+ am_pdp_socket = serializers.CharField(
23
+ default="/var/amavis/amavisd.sock", required=False
24
+ )
25
+ user_can_release = serializers.BooleanField(default=False)
26
+ self_service = serializers.BooleanField(default=False)
27
+ notifications_sender = serializers.EmailField(default="noreply@domain.tld")
28
+ manual_learning = serializers.BooleanField(default=True)
29
+ sa_is_local = serializers.BooleanField(default=True)
30
+ default_user = serializers.CharField(default="amavis", required=False)
31
+ spamd_address = serializers.CharField(default="127.0.0.1", required=False)
32
+ spamd_port = serializers.IntegerField(default=783, required=False)
33
+ domain_level_learning = serializers.BooleanField(default=False)
34
+ user_level_learning = serializers.BooleanField(default=False)
35
+
36
+ def validate(self, data):
37
+ am_pdp_mode = data.get("am_pdp_mode")
38
+ errors = {}
39
+ error = _("This field is required")
40
+ if am_pdp_mode == "inet":
41
+ for name in ["am_pdp_host", "am_pdp_port"]:
42
+ if name not in data:
43
+ errors[name] = error
44
+ else:
45
+ name = "am_pdp_socket"
46
+ if name not in data:
47
+ errors[name] = error
48
+ manual_learning = data.get("manual_learning")
49
+ if manual_learning:
50
+ name = "default_user"
51
+ if name not in data:
52
+ errors[name] = error
53
+ sa_is_local = data.get("sa_is_local")
54
+ if sa_is_local:
55
+ for name in ["spamd_address", "spamd_port"]:
56
+ if name not in data:
57
+ errors[name] = error
58
+ if errors:
59
+ raise serializers.ValidationError(errors)
60
+ return data
61
+
62
+
63
+ class UserPreferencesSerializer(serializers.Serializer):
64
+
65
+ messages_per_page = serializers.IntegerField(default=40)
66
+
67
+
68
+ class HeaderSerializer(serializers.Serializer):
69
+
70
+ name = serializers.CharField()
71
+ value = serializers.CharField()
72
+
73
+
74
+ class MessageHeadersSerializer(serializers.Serializer):
75
+
76
+ headers = HeaderSerializer(many=True)
77
+
78
+
79
+ class MessageSerializer(serializers.Serializer):
80
+
81
+ qtype = serializers.CharField()
82
+ qreason = serializers.CharField()
83
+ headers = HeaderSerializer(many=True)
84
+ body = serializers.CharField()
85
+
86
+
87
+ class CompactMessageSerializer(serializers.Serializer):
88
+
89
+ mailid = serializers.CharField()
90
+ type = serializers.CharField()
91
+ status = serializers.CharField()
92
+ score = serializers.CharField()
93
+ to_address = serializers.CharField()
94
+ from_address = serializers.CharField()
95
+ subject = serializers.CharField()
96
+ datetime = serializers.DateTimeField()
97
+ style = serializers.CharField(required=False)
98
+
99
+
100
+ class PaginatedMessageListSerializer(serializers.Serializer):
101
+
102
+ count = serializers.IntegerField()
103
+ first_index = serializers.IntegerField()
104
+ last_index = serializers.IntegerField()
105
+ prev_page = serializers.IntegerField()
106
+ next_page = serializers.IntegerField()
107
+ results = CompactMessageSerializer(many=True)
108
+
109
+
110
+ class MessageIdentifierSerializer(serializers.Serializer):
111
+
112
+ mailid = serializers.CharField()
113
+ rcpt = serializers.CharField()
114
+ secret_id = serializers.CharField(required=False)
115
+
116
+
117
+ class MessageSelectionSerializer(serializers.Serializer):
118
+
119
+ selection = MessageIdentifierSerializer(many=True)
120
+
121
+
122
+ class MarkMessageSelectionSerializer(MessageSelectionSerializer):
123
+
124
+ type = serializers.CharField()
125
+ database = serializers.CharField(required=False)
126
+
127
+
128
+ class PolicySerializer(serializers.ModelSerializer):
129
+
130
+ class Meta:
131
+ model = models.Policy
132
+ fields = (
133
+ "bypass_virus_checks",
134
+ "bypass_spam_checks",
135
+ "bypass_banned_checks",
136
+ "spam_tag2_level",
137
+ "spam_kill_level",
138
+ "spam_subject_tag2",
139
+ )
@@ -0,0 +1,240 @@
1
+ """SQL connector module."""
2
+
3
+ import datetime
4
+
5
+ from django.db.models import Q
6
+
7
+ from modoboa.admin.models import Domain
8
+ from modoboa.lib.email_utils import decode
9
+
10
+ from .lib import cleanup_email_address, make_query_args
11
+ from .models import Maddr, Msgrcpt, Quarantine
12
+ from .utils import ConvertFrom, fix_utf8_encoding, smart_bytes, smart_str
13
+
14
+
15
+ def reverse_domain_names(domains):
16
+ """Return a list of reversed domain names."""
17
+ return [".".join(reversed(domain.split("."))) for domain in domains]
18
+
19
+
20
+ class SQLconnector:
21
+ """This class handles all database operations."""
22
+
23
+ ORDER_TRANSLATION_TABLE = {
24
+ "type": "content",
25
+ "score": "bspam_level",
26
+ "datetime": "mail__time_num",
27
+ "subject": "mail__subject",
28
+ "from_address": "mail__from_addr",
29
+ "to_address": "rid__email",
30
+ }
31
+
32
+ QUARANTINE_FIELDS = [
33
+ "content",
34
+ "bspam_level",
35
+ "rs",
36
+ "rid__email",
37
+ "mail__from_addr",
38
+ "mail__subject",
39
+ "mail__mail_id",
40
+ "mail__time_num",
41
+ ]
42
+
43
+ def __init__(self, user=None, ordering: str | None = None) -> None:
44
+ """Constructor."""
45
+ self.user = user
46
+ self.messages = None
47
+ self.ordering = ordering
48
+
49
+ self._messages_count = None
50
+ self._annotations = {}
51
+
52
+ def _exec(self, query, args):
53
+ """Execute a raw SQL query.
54
+
55
+ :param string query: query to execute
56
+ :param list args: a list of arguments to replace in :kw:`query`
57
+ """
58
+ from django.db import connections, transaction
59
+
60
+ with transaction.atomic():
61
+ cursor = connections["amavis"].cursor()
62
+ cursor.execute(query, args)
63
+
64
+ def _apply_msgrcpt_simpleuser_filter(self, flt):
65
+ """Apply specific filter for simple users."""
66
+ if "str_email" not in self._annotations:
67
+ self._annotations["str_email"] = ConvertFrom("rid__email")
68
+
69
+ rcpts = [self.user.email]
70
+ if hasattr(self.user, "mailbox"):
71
+ rcpts += self.user.mailbox.alias_addresses
72
+
73
+ query_rcpts = []
74
+ for rcpt in rcpts:
75
+ query_rcpts += make_query_args(rcpt, exact_extension=False, wildcard=".*")
76
+
77
+ re = f"({'|'.join(query_rcpts)})"
78
+ return flt & Q(str_email__regex=re)
79
+
80
+ def _apply_msgrcpt_filters(self, flt):
81
+ """Apply filters based on user's role."""
82
+ if self.user.role == "SimpleUsers":
83
+ flt = self._apply_msgrcpt_simpleuser_filter(flt)
84
+ elif not self.user.is_superuser:
85
+ doms = Domain.objects.get_for_admin(self.user).values_list(
86
+ "name", flat=True
87
+ )
88
+ flt &= Q(rid__domain__in=reverse_domain_names(doms))
89
+ return flt
90
+
91
+ def _get_quarantine_content(self, request):
92
+ """Fetch quarantine content.
93
+
94
+ Filters: rs, rid, content
95
+ """
96
+ flt = (
97
+ Q(rs__in=[" ", "V", "R", "p", "S", "H"])
98
+ if request.GET.get("viewrequests", "0") != "1"
99
+ else Q(rs="p")
100
+ )
101
+ flt = self._apply_msgrcpt_filters(flt)
102
+ search = request.GET.get("search")
103
+ if search:
104
+ criteria = request.GET.get("criteria", "both")
105
+ if criteria == "both":
106
+ criteria = "from_addr,subject,to"
107
+ search_flt = None
108
+ for crit in criteria.split(","):
109
+ if crit == "from_addr":
110
+ nfilter = Q(mail__from_addr__icontains=search)
111
+ elif crit == "subject":
112
+ nfilter = Q(mail__subject__icontains=search)
113
+ elif crit == "to":
114
+ if "str_email" not in self._annotations:
115
+ self._annotations["str_email"] = ConvertFrom("rid__email")
116
+ nfilter = Q(str_email__icontains=search)
117
+ else:
118
+ continue
119
+ search_flt = nfilter if search_flt is None else search_flt | nfilter
120
+ if search_flt:
121
+ flt &= search_flt
122
+
123
+ msgtype = request.GET.get("msgtype")
124
+ if msgtype is not None:
125
+ flt &= Q(content=msgtype)
126
+
127
+ flt &= Q(mail__in=Quarantine.objects.filter(chunk_ind=1).values("mail_id"))
128
+
129
+ return (
130
+ Msgrcpt.objects.annotate(**self._annotations)
131
+ .select_related("mail", "rid")
132
+ .filter(flt)
133
+ )
134
+
135
+ def messages_count(self, request) -> int:
136
+ """
137
+ Return the total number of messages living in the quarantine.
138
+
139
+ We also store the built queryset for a later use.
140
+ """
141
+ if self.user is None:
142
+ return None
143
+ if self._messages_count is None:
144
+ self.messages = self._get_quarantine_content(request)
145
+ self.messages = self.messages.values(*self.QUARANTINE_FIELDS)
146
+
147
+ order = self.ordering
148
+ if order is not None:
149
+ sign = ""
150
+ if order[0] == "-":
151
+ sign = "-"
152
+ order = order[1:]
153
+ order = self.ORDER_TRANSLATION_TABLE.get(order)
154
+ if order:
155
+ self.messages = self.messages.order_by(sign + order)
156
+
157
+ self._messages_count = len(self.messages)
158
+
159
+ return self._messages_count
160
+
161
+ def fetch(self, start=None, stop=None):
162
+ """Fetch a range of messages from the internal cache."""
163
+ emails = []
164
+ for qm in self.messages[start - 1 : stop]:
165
+ if qm["rs"] == "D":
166
+ continue
167
+ m = {
168
+ "from_address": cleanup_email_address(
169
+ fix_utf8_encoding(qm["mail__from_addr"])
170
+ ),
171
+ "to_address": smart_str(qm["rid__email"]),
172
+ "subject": fix_utf8_encoding(qm["mail__subject"]),
173
+ "mailid": smart_str(qm["mail__mail_id"]),
174
+ "datetime": datetime.datetime.fromtimestamp(qm["mail__time_num"]),
175
+ "type": qm["content"],
176
+ "score": qm["bspam_level"],
177
+ "status": qm["rs"],
178
+ }
179
+ if qm["rs"] in ["", " "]:
180
+ m["style"] = "unseen"
181
+ elif qm["rs"] == "p":
182
+ m["style"] = "pending"
183
+ emails.append(m)
184
+ return emails
185
+
186
+ def get_recipient_message(self, address, mailid):
187
+ """Retrieve a message for a given recipient."""
188
+ assert isinstance(address, str), "address should be of type str"
189
+
190
+ return Msgrcpt.objects.annotate(str_email=ConvertFrom("rid__email")).get(
191
+ mail=mailid.encode("ascii"), str_email=address
192
+ )
193
+
194
+ def set_msgrcpt_status(self, address, mailid: str, status):
195
+ """Change the status (rs field) of a message recipient.
196
+
197
+ :param string status: status
198
+ """
199
+ assert isinstance(address, str), "address should be of type str"
200
+ addr = Maddr.objects.annotate(str_email=ConvertFrom("email")).get(
201
+ str_email=address
202
+ )
203
+ self._exec(
204
+ "UPDATE msgrcpt SET rs=%s WHERE mail_id=%s AND rid=%s",
205
+ [status, mailid.encode("ascii"), addr.id],
206
+ )
207
+
208
+ def get_domains_pending_requests(self, domains):
209
+ """Retrieve pending release requests for a list of domains."""
210
+ return Msgrcpt.objects.filter(
211
+ rs="p", rid__domain__in=reverse_domain_names(domains)
212
+ )
213
+
214
+ def get_pending_requests(self):
215
+ """Return the number of requests currently pending."""
216
+ rq = Q(rs="p")
217
+ if not self.user.is_superuser:
218
+ doms = Domain.objects.get_for_admin(self.user)
219
+ if not doms.exists():
220
+ return 0
221
+ doms_q = Q(
222
+ rid__domain__in=reverse_domain_names(
223
+ doms.values_list("name", flat=True)
224
+ )
225
+ )
226
+ rq &= doms_q
227
+ return Msgrcpt.objects.filter(rq).count()
228
+
229
+ def get_mail_content(self, mailid) -> str:
230
+ """Retrieve the content of a message."""
231
+ content_bytes = smart_bytes("").join(
232
+ [
233
+ smart_bytes(qmail.mail_text)
234
+ for qmail in Quarantine.objects.filter(mail=mailid)
235
+ ]
236
+ )
237
+ content = decode(
238
+ content_bytes, "utf-8", append_to_error=f"; mail_id={smart_str(mailid)}"
239
+ )
240
+ return content
@@ -0,0 +1,66 @@
1
+ """An email representation based on a database record."""
2
+
3
+ from html2text import HTML2Text
4
+
5
+ from django.template.loader import render_to_string
6
+
7
+ from modoboa.lib.email_utils import Email
8
+ from .sql_connector import SQLconnector
9
+ from .utils import fix_utf8_encoding, smart_str
10
+
11
+
12
+ class SQLemail(Email):
13
+ """The SQL version of the Email class."""
14
+
15
+ def __init__(self, *args, **kwargs):
16
+ super().__init__(*args, **kwargs)
17
+ self.qtype = ""
18
+ self.qreason = ""
19
+
20
+ qreason = self.msg["X-Amavis-Alert"]
21
+ if qreason:
22
+ if "," in qreason:
23
+ self.qtype, qreason = qreason.split(",", 1)
24
+ elif qreason.startswith("BAD HEADER SECTION "):
25
+ # Workaround for amavis <= 2.8.0 :p
26
+ self.qtype = "BAD HEADER SECTION"
27
+ qreason = qreason[19:]
28
+
29
+ qreason = " ".join([x.strip() for x in qreason.splitlines()])
30
+ self.qreason = qreason
31
+
32
+ def _fetch_message(self) -> str:
33
+ return SQLconnector().get_mail_content(self.mailid)
34
+
35
+ @property
36
+ def body(self):
37
+ if self._body is None:
38
+ super().body # noqa
39
+ self._body = fix_utf8_encoding(self._body)
40
+
41
+ # if there's no plain text version available attempt to make one by
42
+ # sanitising the html version. The output isn't always pretty but it
43
+ # is readable, better than a blank screen and helps the user decide
44
+ # if the message is spam or ham.
45
+ if (
46
+ self.dformat == "plain"
47
+ and not self.contents["plain"]
48
+ and self.contents["html"]
49
+ ):
50
+ h = HTML2Text()
51
+ h.ignore_tables = True
52
+ h.images_to_alt = True
53
+ mail_text = h.handle(self.contents["html"])
54
+ self.contents["plain"] = self._post_process_plain(smart_str(mail_text))
55
+ self._body = self.viewmail_plain()
56
+ self._body = fix_utf8_encoding(self._body)
57
+
58
+ return self._body
59
+
60
+ def render_headers(self, **kwargs):
61
+ context = {
62
+ "qtype": self.qtype,
63
+ "qreason": self.qreason,
64
+ "headers": self.headers,
65
+ }
66
+ return render_to_string("modoboa_amavis/mailheaders.html", context)
@@ -0,0 +1,33 @@
1
+ """Async tasks."""
2
+
3
+ # from django.utils.translation import ngettext
4
+
5
+ from modoboa.core import models as core_models
6
+
7
+ from .lib import SpamassassinClient
8
+ from .sql_connector import SQLconnector
9
+
10
+
11
+ def manual_learning(user_pk: int, mtype: str, selection: list[dict], recipient_db: str):
12
+ """Task to trigger manual learning for given selection."""
13
+ user = core_models.User.objects.get(pk=user_pk)
14
+ connector = SQLconnector()
15
+ saclient = SpamassassinClient(user, recipient_db)
16
+ for item in selection:
17
+ content = connector.get_mail_content(item["mailid"].encode("ascii"))
18
+ result = (
19
+ saclient.learn_spam(item["rcpt"], content)
20
+ if mtype == "spam"
21
+ else saclient.learn_ham(item["rcpt"], content)
22
+ )
23
+ if not result:
24
+ break
25
+ connector.set_msgrcpt_status(item["rcpt"], item["mailid"], mtype[0].upper())
26
+ if saclient.error is None:
27
+ saclient.done()
28
+
29
+ # message = ngettext("%(count)d message processed successfully",
30
+ # "%(count)d messages processed successfully",
31
+ # len(selection)) % {"count": len(selection)}
32
+ # else:
33
+ # message = saclient.error
@@ -0,0 +1,16 @@
1
+ {% load i18n %}
2
+
3
+ {% blocktranslate trimmed count counter=total %}
4
+ {{ counter }} release request is pending for action.
5
+ {% plural %}
6
+ {{ counter }} release requests are pending for action.
7
+ {% endblocktranslate %}
8
+ {% translate "Sketch:" %}
9
+ {% for msg in requests %}
10
+ {% translate "From:" %} {{ msg.mail.sid.email }}
11
+ {% translate "To:" %} {{ msg.rid.email }}
12
+ {% translate "Date:" %} {{ msg.mail.time_datetime|date:"DATETIME_FORMAT" }}
13
+ {% translate "Subject:" %} {{ msg.mail.subject }}
14
+ {% translate "Act on this message:" %} {{ baseurl }}/user/quarantine/{{ msg.mail.mail_id }}/{{ msg.rid.email }}
15
+ {% endfor %}
16
+ {% blocktranslate %}Please visit {{ listingurl }} for a full listing.{% endblocktranslate %}
File without changes
@@ -0,0 +1,3 @@
1
+ #!/bin/sh
2
+
3
+ exit 0
@@ -0,0 +1,80 @@
1
+ X-Envelope-From: <it-edu@evil.example.net>
2
+ X-Envelope-To: <me@example.net>
3
+ X-Envelope-To-Blocked: <me@example.net>
4
+ X-Quarantine-ID: <nZkKQ_MUuyil>
5
+ X-Amavis-Alert: BAD HEADER SECTION, Non-encoded non-ASCII data (and not UTF-8)
6
+ (char 85 hex): Subject: I think I saw you in my dreams\x{85}
7
+ X-Spam-Flag: YES
8
+ X-Spam-Score: 8.178
9
+ X-Spam-Level: ********
10
+ X-Spam-Status: Yes, score=8.178 tag=-999 tag2=3.9 kill=5
11
+ tests=[HEADER_FROM_DIFFERENT_DOMAINS=0.25, HTML_IMAGE_ONLY_28=0.726,
12
+ HTML_MESSAGE=0.001, MIME_HTML_ONLY=1.105, RCVD_IN_MSPIKE_H2=-0.001,
13
+ TVD_PH_BODY_ACCOUNTS_PRE=0.001, T_REMOTE_IMAGE=0.01,
14
+ T_RP_MATCHES_RCVD=-0.01, URIBL_BLACK=1.7, URIBL_DBL_ABUSE_SPAM=2,
15
+ URI_WPADMIN=2.396] autolearn=no autolearn_force=no
16
+ Received: from mail.example.net ([127.0.0.1])
17
+ by mail.example.net (mail.example.net [127.0.0.1]) (amavisd-new, port 10024)
18
+ with LMTP id nZkKQ_MUuyil for <me@example.net>;
19
+ Sat, 16 Dec 2017 13:09:49 +0000 (GMT)
20
+ Received: from evil.example.net (mail.evil.example.net [10.0.0.1])
21
+ by mail.example.net (Postfix) with ESMTPS id 62B2E1AF12D
22
+ for <me@example.net>; Sat, 16 Dec 2017 13:09:49 +0000 (GMT)
23
+ Received: from it-edu by evil.example.net with local (Exim 4.82)
24
+ (envelope-from <it-edu@evil.example.net>)
25
+ id 1eQCDr-0005NE-IT
26
+ for me@example.net; Sat, 16 Dec 2017 16:09:47 +0300
27
+ To: me@example.net
28
+ Subject: =?UTF-8?Q?Account_Suspension_Notification_=21?=
29
+ X-PHP-Originating-Script: 520:maill.php
30
+ Date: Sat, 16 Dec 2017 16:09:47 +0300
31
+ From: Notice <Notice@fake.example.net>
32
+ Reply-To: Notice@fake.example.net
33
+ Message-ID: <6619a5295cefcf484a58944fc280eb90@evil.example.net>
34
+ X-Priority: 1
35
+ X-Mailer: PHPMailer 5.2.10 (https://github.com/PHPMailer/PHPMailer/)
36
+ MIME-Version: 1.0
37
+ Content-Type: text/html; charset=iso-8859-1
38
+ Content-Transfer-Encoding: 8bit
39
+
40
+ <!DOCTYPE html>
41
+ <html>
42
+ <HEAD><STYLE>
43
+ .cssage P
44
+ {
45
+ margin:0px;
46
+ padding:0px
47
+ }
48
+ body.cssage
49
+ {
50
+ font-size: 12pt;
51
+ font-family:Calibri
52
+ }
53
+ </STYLE>
54
+
55
+ </HEAD>
56
+ <BODY class="cssage">
57
+ <DIV dir="ltr"><FONT size=2 face="Arial, Helvetica, sans-serif"></FONT><BR>
58
+ <DIV>
59
+ <DIV>
60
+ <TABLE style="FONT-FAMILY: 'Times New Roman'" border=0 cellSpacing=0 cellPadding=0 width=600 align="center">
61
+ <TR class=header>
62
+ <TD vAlign=bottom><SPAN style="FONT-FAMILY: 'Trebuchet MS', Arial, Helvetica, sans-serif; COLOR: rgb(159,159,159); FONT-SIZE: 12px" >This is an automated email, please do not reply</SPAN></TD>
63
+ <TD align=right><IMG style="WIDTH: 215px; HEIGHT: 66px" alt="" src="http://im55.gulfup.com/oLrUdE.jpg"></TD></TR>
64
+ <TR >
65
+ <TD height=14 vAlign=bottom colSpan=2><IMG src="https://www.payza.com/emails/images/bar.png" width="100%" height=3></TD></TR>
66
+ <TR>
67
+ <TD colSpan=2>
68
+ <H1 style="PADDING-BOTTOM: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; FONT-FAMILY: 'Trebuchet MS', Arial, Helvetica, sans-serif; COLOR: rgb(189,151,0); FONT-SIZE: 24px; FONT-WEIGHT: normal; PADDING-TOP: 10px">Dear Client</H1>
69
+ <P style="LINE-HEIGHT: 21px; FONT-FAMILY: 'Trebuchet MS', Arial, Helvetica, sans-serif; COLOR: rgb(84,84,84); FONT-SIZE: 14px">We've noticed that some of your account information appears to be missing or incorrect
70
+ We need to verify your account information in order to continue using your Apple ID, Please Verify your account information by clicking on the link below<BR><BR><B><A style="COLOR: rgb(27,144,234)" href="http://chiikulab.com/wp-admin/images/Update/Apple2018%20V2.1/
71
+ " target=_blank>Click here to Verify your ID</A></B></P></TD></TR>
72
+ <TR>
73
+ <TD colSpan=2>
74
+ <DIV><SPAN style="FONT-FAMILY: 'Trebuchet MS', Arial, Helvetica, sans-serif; COLOR: rgb(51,102,204); FONT-SIZE: 15px" ></SPAN><BR></DIV><br><SPAN style="FONT-FAMILY: 'Trebuchet MS', Arial, Helvetica, sans-serif; COLOR: rgb(202,162,0); FONT-SIZE: 17px" class=ecxthanks>Thanks for choosing Apple,</SPAN><BR style="COLOR: rgb(202,162,0)"><SPAN style="FONT-FAMILY: 'Trebuchet MS', Arial, Helvetica, sans-serif; COLOR: rgb(202,162,0); FONT-SIZE: 15px" >Apple Team</SPAN></TD></TR>
75
+ <TR >
76
+ <TD height=14 vAlign=bottom colSpan=2><IMG src="https://www.payza.com/emails/images/bar.png" width="100%" height=3></TD></TR>
77
+ <TR>
78
+ <TD colSpan=2>
79
+ <P style="PADDING-BOTTOM: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; FONT-FAMILY: 'Trebuchet MS', Arial, Helvetica, sans-serif; COLOR: rgb(84,84,84); FONT-SIZE: 12px; PADDING-TOP: 10px" >&copy; 2017 Apple. All rights reserved.</P>
80
+ <P style="FONT-FAMILY: 'Trebuchet MS', Arial, Helvetica, sans-serif; COLOR: rgb(186,148,0); FONT-SIZE: 11px">Email ID: 163327</P></TD></TR></TABLE></DIV></DIV></DIV></BODY></html>
@@ -0,0 +1,17 @@
1
+ <pre>This is an automated email, please do not reply
2
+
3
+ # Dear Client
4
+
5
+ We&#x27;ve noticed that some of your account information appears to be missing or
6
+ incorrect We need to verify your account information in order to continue
7
+ using your Apple ID, Please Verify your account information by clicking on the
8
+ link below
9
+
10
+ **Click here to Verify your ID**
11
+
12
+ Thanks for choosing Apple,
13
+ Apple Team
14
+
15
+ © 2017 Apple. All rights reserved.
16
+
17
+ Email ID: 163327</pre>
@@ -0,0 +1,3 @@
1
+ #!/bin/sh
2
+
3
+ exit 5
@@ -0,0 +1,25 @@
1
+ from django.test import TestCase
2
+ from django.test.utils import override_settings
3
+
4
+ from ..checks import settings_checks
5
+
6
+
7
+ class CheckSessionCookieSecureTest(TestCase):
8
+
9
+ databases = "__all__"
10
+
11
+ @override_settings(AMAVIS_DEFAULT_DATABASE_ENCODING="LATIN-1")
12
+ def test_amavis_database_encoding_incorrect(self):
13
+ """
14
+ If AMAVIS_DEFAULT_DATABASE_ENCODING is incorrect provide one warning.
15
+ """
16
+ self.assertEqual(
17
+ settings_checks.check_amavis_database_encoding(None), [settings_checks.W001]
18
+ )
19
+
20
+ @override_settings(AMAVIS_DEFAULT_DATABASE_ENCODING="UTF-8")
21
+ def test_amavis_database_encoding_correct(self):
22
+ """
23
+ If AMAVIS_DEFAULT_DATABASE_ENCODING is correct, there's no warning.
24
+ """
25
+ self.assertEqual(settings_checks.check_amavis_database_encoding(None), [])