modoboa 2.4.11__py3-none-any.whl → 2.5.0__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 (314) 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 +3 -1
  43. modoboa/core/api/v2/viewsets.py +24 -3
  44. modoboa/core/handlers.py +6 -2
  45. modoboa/core/management/commands/add_allowed_hosts.py +33 -0
  46. modoboa/core/management/commands/load_initial_data.py +10 -0
  47. modoboa/core/migrations/0025_rename_user_email_is_active_core_user_email_c0c03f_idx.py +23 -5
  48. modoboa/core/tests/test_core.py +24 -0
  49. modoboa/core/utils.py +3 -0
  50. modoboa/frontend_dist/assets/AccountAliasForm-BuSy_1n9.js +1 -0
  51. modoboa/frontend_dist/assets/AccountEditView-qdJmLM_e.js +1 -0
  52. modoboa/frontend_dist/assets/AccountLayout-DrN7vHsX.js +1 -0
  53. modoboa/frontend_dist/assets/AccountPasswordSubForm-DZGt_Xgq.js +1 -0
  54. modoboa/frontend_dist/assets/AccountView-CO65y0vZ.js +1 -0
  55. modoboa/frontend_dist/assets/AddressBook-BZNUlhek.js +1 -0
  56. modoboa/frontend_dist/assets/AdminLayout-CTNhuwTw.js +1 -0
  57. modoboa/frontend_dist/assets/AlarmsView-9yKGbmkC.css +1 -0
  58. modoboa/frontend_dist/assets/AlarmsView-DN_JIw9g.js +1 -0
  59. modoboa/frontend_dist/assets/AliasEditView-DjpPUTp9.js +1 -0
  60. modoboa/frontend_dist/assets/{AliasRecipientForm-IOae6sjF.js → AliasRecipientForm-B1Y8wFdP.js} +1 -1
  61. modoboa/frontend_dist/assets/AliasView-GOJ5lyQH.js +1 -0
  62. modoboa/frontend_dist/assets/AuditTrailView-fbXmq70e.js +1 -0
  63. modoboa/frontend_dist/assets/CalendarView-LlQQNEPL.js +1 -0
  64. modoboa/frontend_dist/assets/{ChoiceField-DJ_c78Cm.js → ChoiceField-B3ReQHVe.js} +1 -1
  65. modoboa/frontend_dist/assets/ComposeEmailForm-Bs1fZXAL.js +1 -0
  66. modoboa/frontend_dist/assets/ComposeEmailView-s3LMl3pO.js +1 -0
  67. modoboa/frontend_dist/assets/ConfirmDialog-DY_kUHLG.js +1 -0
  68. modoboa/frontend_dist/assets/{ConnectedLayout-Dvwmicnc.css → ConnectedLayout-Bxh21hcH.css} +1 -1
  69. modoboa/frontend_dist/assets/ConnectedLayout-UWjiYBNw.js +1 -0
  70. modoboa/frontend_dist/assets/CreationForm-ORg3fazt.js +1 -0
  71. modoboa/frontend_dist/assets/DashboardView-Dplk9itS.js +1 -0
  72. modoboa/frontend_dist/assets/DomainAdminList-DVn9x0rB.js +1 -0
  73. modoboa/frontend_dist/assets/DomainEditView-nAoL64D_.js +1 -0
  74. modoboa/frontend_dist/assets/{DomainTransportForm-C2xo0Yd7.js → DomainTransportForm-CA-DNUxX.js} +1 -1
  75. modoboa/frontend_dist/assets/{DomainView-BDKoBFYr.css → DomainView-CCLYXPHx.css} +1 -1
  76. modoboa/frontend_dist/assets/DomainView-CdXPpwJG.js +5 -0
  77. modoboa/frontend_dist/assets/DomainsView-B_59gowf.js +1 -0
  78. modoboa/frontend_dist/assets/DomainsView-DZ-ss9bI.css +1 -0
  79. modoboa/frontend_dist/assets/EmailField-CwcwI5xW.js +1 -0
  80. modoboa/frontend_dist/assets/EmailView-BshxcfAK.js +1 -0
  81. modoboa/frontend_dist/assets/EmptyLayout-DFfhnhLi.js +1 -0
  82. modoboa/frontend_dist/assets/FiltersView-Cf20MSTK.js +1 -0
  83. modoboa/frontend_dist/assets/ForwardEmailView-CZG062os.js +1 -0
  84. modoboa/frontend_dist/assets/{HtmlEditor-CJ9umKeO.js → HtmlEditor-Bh4c689R.js} +1 -1
  85. modoboa/frontend_dist/assets/IdentitiesView-BXAuU1YX.js +1 -0
  86. modoboa/frontend_dist/assets/{IdentitiesView-0ziuQ5s-.css → IdentitiesView-DPrrRMS5.css} +1 -1
  87. modoboa/frontend_dist/assets/InformationView-C9vvvQhJ.css +1 -0
  88. modoboa/frontend_dist/assets/InformationView-Cn5FZW7H.js +1 -0
  89. modoboa/frontend_dist/assets/{LoadingData-CYwX3Jpn.js → LoadingData-CdVvm4FI.js} +1 -1
  90. modoboa/frontend_dist/assets/{LoginCallbackView-E01qkKn0.js → LoginCallbackView-B9hAH4MI.js} +1 -1
  91. modoboa/frontend_dist/assets/{LoginView-Cy4uFV9h.js → LoginView-tHIR4Adc.js} +1 -1
  92. modoboa/frontend_dist/assets/MailboxView-Bugu2vhg.js +1 -0
  93. modoboa/frontend_dist/assets/MenuItems-PXjiG-fs.js +1 -0
  94. modoboa/frontend_dist/assets/MessageView-Cy4STShm.js +1 -0
  95. modoboa/frontend_dist/assets/MessagesView-DdkuEgfX.js +1 -0
  96. modoboa/frontend_dist/assets/MigrationsView-CidSEjCF.js +1 -0
  97. modoboa/frontend_dist/assets/{ParametersForm-BZM0QSvg.js → ParametersForm-CAv4SH-E.js} +1 -1
  98. modoboa/frontend_dist/assets/ParametersView-CX7Ffemw.js +1 -0
  99. modoboa/frontend_dist/assets/ParametersView-CrbNcmV3.js +1 -0
  100. modoboa/frontend_dist/assets/ProviderEditView-CrltAQXl.js +1 -0
  101. modoboa/frontend_dist/assets/ProviderGeneralForm-BYAzVnXM.js +1 -0
  102. modoboa/frontend_dist/assets/ProvidersView-osjIY4Ex.js +1 -0
  103. modoboa/frontend_dist/assets/QuarantineLayout-B8EcU9vS.js +1 -0
  104. modoboa/frontend_dist/assets/QuarantineView-D4gOE4EQ.css +1 -0
  105. modoboa/frontend_dist/assets/QuarantineView-D8Qg0MXA.js +1 -0
  106. modoboa/frontend_dist/assets/ReplyEmailView-BABPqWhd.js +1 -0
  107. modoboa/frontend_dist/assets/ResourcesForm-OaqdRYVs.js +1 -0
  108. modoboa/frontend_dist/assets/SelfServiceLayout-d277YTGR.js +1 -0
  109. modoboa/frontend_dist/assets/{SettingsView-BxLJBFY0.js → SettingsView-9iNcDhkI.js} +2 -2
  110. modoboa/frontend_dist/assets/StatisticsView-cHsPyGkL.js +1 -0
  111. modoboa/frontend_dist/assets/TimeSerieChart--V83dcJ9.js +1 -0
  112. modoboa/frontend_dist/assets/UserLayout-B3sBiTcZ.js +1 -0
  113. modoboa/frontend_dist/assets/VAlert-BuaaYN2h.js +1 -0
  114. modoboa/frontend_dist/assets/VApp-CKP-6zGP.js +1 -0
  115. modoboa/frontend_dist/assets/VAutocomplete-Dwv6_Rzq.js +1 -0
  116. modoboa/frontend_dist/assets/VAvatar-Cmga0vj6.js +1 -0
  117. modoboa/frontend_dist/assets/VBadge-BQrRJ9S0.css +1 -0
  118. modoboa/frontend_dist/assets/VBadge-CixeK87a.js +1 -0
  119. modoboa/frontend_dist/assets/VCard-CxH9DWoK.js +1 -0
  120. modoboa/frontend_dist/assets/VCheckbox-62GOpvvP.js +1 -0
  121. modoboa/frontend_dist/assets/{VCheckboxBtn-Dt810gWf.js → VCheckboxBtn-DMoNtKT8.js} +1 -1
  122. modoboa/frontend_dist/assets/VChip-D_styETR.js +1 -0
  123. modoboa/frontend_dist/assets/VColorPicker-BHscBGQV.js +1 -0
  124. modoboa/frontend_dist/assets/{VContainer-DvTbsotR.js → VContainer-B46caNs1.js} +1 -1
  125. modoboa/frontend_dist/assets/VDataTable-Bh8NbVSx.js +1 -0
  126. modoboa/frontend_dist/assets/VDataTableServer-BDR5hOmo.js +1 -0
  127. modoboa/frontend_dist/assets/VDataTableVirtual-BOQlNtIG.js +1 -0
  128. modoboa/frontend_dist/assets/{VDialog-Bk6EWNhz.js → VDialog-BcTg7w6P.js} +1 -1
  129. modoboa/frontend_dist/assets/VExpansionPanels-BmH5Jl2Z.js +1 -0
  130. modoboa/frontend_dist/assets/VFileInput-BC4yAygd.js +1 -0
  131. modoboa/frontend_dist/assets/VForm-D5iPGkde.js +1 -0
  132. modoboa/frontend_dist/assets/VInput-CcxkaOXT.css +1 -0
  133. modoboa/frontend_dist/assets/VInput-CoDJzvaW.js +1 -0
  134. modoboa/frontend_dist/assets/VMenu-gUG70-zD.js +1 -0
  135. modoboa/frontend_dist/assets/VPicker-BXuKT3zB.js +1 -0
  136. modoboa/frontend_dist/assets/VProgressCircular-BtOPiGCg.js +1 -0
  137. modoboa/frontend_dist/assets/VRadioGroup-DIFZKSn-.js +1 -0
  138. modoboa/frontend_dist/assets/{VRow-BF35mT1S.js → VRow-ozg66L7j.js} +1 -1
  139. modoboa/frontend_dist/assets/VSelect-C3RjAa45.js +1 -0
  140. modoboa/frontend_dist/assets/VSelectionControl-zyz-fJvC.js +1 -0
  141. modoboa/frontend_dist/assets/VSheet-BNx2X4Mk.js +1 -0
  142. modoboa/frontend_dist/assets/VSpacer-DinPiXs9.js +1 -0
  143. modoboa/frontend_dist/assets/VSwitch-DwxdeAEq.js +1 -0
  144. modoboa/frontend_dist/assets/VTable-DaLxa4FO.js +1 -0
  145. modoboa/frontend_dist/assets/VTabs-BP0Hgsgm.js +1 -0
  146. modoboa/frontend_dist/assets/VTextField-BzBVKKob.css +1 -0
  147. modoboa/frontend_dist/assets/VTextField-XoGTj1KG.js +1 -0
  148. modoboa/frontend_dist/assets/VTextarea-wBlRMIv_.js +1 -0
  149. modoboa/frontend_dist/assets/VToolbar-CFZfqeOr.js +1 -0
  150. modoboa/frontend_dist/assets/VWindowItem-BB7ETW3b.js +1 -0
  151. modoboa/frontend_dist/assets/WebmailLayout-_Hk1XhVq.js +1 -0
  152. modoboa/frontend_dist/assets/accounts-DUzbx6k8.js +1 -0
  153. modoboa/frontend_dist/assets/{admin-o-HRGnmT.js → admin-DewTk2H8.js} +1 -1
  154. modoboa/frontend_dist/assets/{aliases-DDVeehyg.js → aliases-4sXmjwXp.js} +1 -1
  155. modoboa/frontend_dist/assets/amavis-CC0li7_T.js +1 -0
  156. modoboa/frontend_dist/assets/amavis-DK8SHE6o.js +1 -0
  157. modoboa/frontend_dist/assets/{contacts-C84DY-Q1.js → contacts-BjghrPqZ.js} +1 -1
  158. modoboa/frontend_dist/assets/{domains-Bgn4ixHL.js → domains-BSawReeu.js} +1 -1
  159. modoboa/frontend_dist/assets/{domains.store-DTE-V7Y1.js → domains.store-D-vWCEIK.js} +1 -1
  160. modoboa/frontend_dist/assets/{filter-CnffiQAW.js → filter-C82FUCw_.js} +1 -1
  161. modoboa/frontend_dist/assets/forwardRefs-cvcnlhoK.js +1 -0
  162. modoboa/frontend_dist/assets/global.store-DbkcI5o2.js +1 -0
  163. modoboa/frontend_dist/assets/{importExport-BlQYb0NO.js → importExport-DzoL4Mvc.js} +1 -1
  164. modoboa/frontend_dist/assets/index-BImkz5Jx.js +984 -0
  165. modoboa/frontend_dist/assets/layout-C5FyYCHK.js +1 -0
  166. modoboa/frontend_dist/assets/{layout.store-DkjrAoXt.js → layout.store-NXWtFIwL.js} +1 -1
  167. modoboa/frontend_dist/assets/{logos-q8SEyAa4.js → logos-BswdveCV.js} +1 -1
  168. modoboa/frontend_dist/assets/{logs-B7IJ7LBa.js → logs-6CbtfaZS.js} +1 -1
  169. modoboa/frontend_dist/assets/{parameters-A6iBEYQq.js → parameters-aSQiR7kN.js} +1 -1
  170. modoboa/frontend_dist/assets/{parameters.store-BiXS4_6w.js → parameters.store-CzQqVatx.js} +1 -1
  171. modoboa/frontend_dist/assets/permissions-DNoefz-n.js +1 -0
  172. modoboa/frontend_dist/assets/{ssrBoot-AzTdjPjk.js → ssrBoot-CKUX4kcb.js} +1 -1
  173. modoboa/frontend_dist/assets/{tag-BnSYRTcD.js → tag-B_yWNNJD.js} +1 -1
  174. modoboa/frontend_dist/assets/transports-BDNB9wR5.js +1 -0
  175. modoboa/frontend_dist/assets/{webmail-CSH_3l6R.js → webmail-CdU6CD9b.js} +1 -1
  176. modoboa/frontend_dist/index.html +1 -1
  177. modoboa/lib/email_utils.py +2 -2
  178. modoboa/lib/permissions.py +7 -0
  179. modoboa/lib/test_runners.py +29 -0
  180. modoboa/lib/tests/__init__.py +5 -1
  181. modoboa/locale/br/LC_MESSAGES/django.po +87 -75
  182. modoboa/locale/cs/LC_MESSAGES/django.po +82 -74
  183. modoboa/locale/cs_CZ/LC_MESSAGES/django.po +145 -121
  184. modoboa/locale/de/LC_MESSAGES/django.mo +0 -0
  185. modoboa/locale/de/LC_MESSAGES/django.po +339 -651
  186. modoboa/locale/de_DE/LC_MESSAGES/django.po +87 -75
  187. modoboa/locale/el_GR/LC_MESSAGES/django.po +160 -135
  188. modoboa/locale/en/LC_MESSAGES/django.po +82 -74
  189. modoboa/locale/es/LC_MESSAGES/django.po +158 -131
  190. modoboa/locale/es_MX/LC_MESSAGES/django.po +82 -74
  191. modoboa/locale/fi/LC_MESSAGES/django.po +87 -75
  192. modoboa/locale/fr/LC_MESSAGES/django.mo +0 -0
  193. modoboa/locale/fr/LC_MESSAGES/django.po +469 -201
  194. modoboa/locale/hu/LC_MESSAGES/django.po +82 -74
  195. modoboa/locale/it/LC_MESSAGES/django.mo +0 -0
  196. modoboa/locale/it/LC_MESSAGES/django.po +148 -122
  197. modoboa/locale/ja_JP/LC_MESSAGES/django.mo +0 -0
  198. modoboa/locale/ja_JP/LC_MESSAGES/django.po +80 -72
  199. modoboa/locale/ka/LC_MESSAGES/django.po +82 -74
  200. modoboa/locale/nl_NL/LC_MESSAGES/django.po +160 -132
  201. modoboa/locale/no/LC_MESSAGES/django.po +82 -74
  202. modoboa/locale/pl_PL/LC_MESSAGES/django.mo +0 -0
  203. modoboa/locale/pl_PL/LC_MESSAGES/django.po +172 -149
  204. modoboa/locale/pt_BR/LC_MESSAGES/django.mo +0 -0
  205. modoboa/locale/pt_BR/LC_MESSAGES/django.po +172 -144
  206. modoboa/locale/pt_PT/LC_MESSAGES/django.po +135 -112
  207. modoboa/locale/ro_RO/LC_MESSAGES/django.po +87 -75
  208. modoboa/locale/ru/LC_MESSAGES/django.po +142 -118
  209. modoboa/locale/si/LC_MESSAGES/django.po +82 -74
  210. modoboa/locale/sk/LC_MESSAGES/django.po +82 -74
  211. modoboa/locale/sk_SK/LC_MESSAGES/django.po +84 -76
  212. modoboa/locale/sl_SI/LC_MESSAGES/django.po +90 -76
  213. modoboa/locale/sv/LC_MESSAGES/django.mo +0 -0
  214. modoboa/locale/sv/LC_MESSAGES/django.po +172 -139
  215. modoboa/locale/tr/LC_MESSAGES/django.po +87 -75
  216. modoboa/locale/tr_TR/LC_MESSAGES/django.po +84 -74
  217. modoboa/locale/uk/LC_MESSAGES/django.po +82 -74
  218. modoboa/locale/zh/LC_MESSAGES/django.po +82 -74
  219. modoboa/locale/zh_CN/LC_MESSAGES/django.po +82 -74
  220. modoboa/locale/zh_TW/LC_MESSAGES/django.po +87 -75
  221. modoboa/parameters/api/v2/tests.py +2 -2
  222. modoboa/parameters/api/v2/viewsets.py +2 -0
  223. modoboa/policyd/tests.py +2 -0
  224. modoboa/urls_api_v2.py +6 -0
  225. {modoboa-2.4.11.dist-info → modoboa-2.5.0.dist-info}/METADATA +6 -4
  226. {modoboa-2.4.11.dist-info → modoboa-2.5.0.dist-info}/RECORD +231 -181
  227. modoboa/frontend_dist/assets/AccountAliasForm-BV6KvTu6.js +0 -1
  228. modoboa/frontend_dist/assets/AccountEditView-DDOFyfBD.js +0 -1
  229. modoboa/frontend_dist/assets/AccountLayout-rX51xgxT.js +0 -1
  230. modoboa/frontend_dist/assets/AccountPasswordSubForm-D9S6LaeH.js +0 -1
  231. modoboa/frontend_dist/assets/AccountView-cmvaZNq3.js +0 -1
  232. modoboa/frontend_dist/assets/AddressBook-DCJxL8SU.js +0 -1
  233. modoboa/frontend_dist/assets/AdminLayout-r0wfG2lO.js +0 -1
  234. modoboa/frontend_dist/assets/AlarmsView-Bheey-gp.css +0 -1
  235. modoboa/frontend_dist/assets/AlarmsView-C0bqC4PA.js +0 -1
  236. modoboa/frontend_dist/assets/AliasEditView-DVoWoCGY.js +0 -1
  237. modoboa/frontend_dist/assets/AliasView-DrONZXOh.js +0 -1
  238. modoboa/frontend_dist/assets/AuditTrailView-OTkoZaMU.js +0 -1
  239. modoboa/frontend_dist/assets/CalendarView-CqF4_Ui9.js +0 -1
  240. modoboa/frontend_dist/assets/ComposeEmailForm-DO5_GB3e.js +0 -1
  241. modoboa/frontend_dist/assets/ComposeEmailView-A91HCBsN.js +0 -1
  242. modoboa/frontend_dist/assets/ConfirmDialog-BBcgdAnO.js +0 -1
  243. modoboa/frontend_dist/assets/ConnectedLayout-1oRW-Rql.js +0 -1
  244. modoboa/frontend_dist/assets/CreationForm-71YJbjsA.js +0 -1
  245. modoboa/frontend_dist/assets/DashboardView-CdLpSfUl.js +0 -1
  246. modoboa/frontend_dist/assets/DomainAdminList-BjC4KsqI.js +0 -1
  247. modoboa/frontend_dist/assets/DomainEditView-CQjKwYxl.js +0 -1
  248. modoboa/frontend_dist/assets/DomainView-BhhuZI_N.js +0 -5
  249. modoboa/frontend_dist/assets/DomainsView-Cft4BP8Z.js +0 -1
  250. modoboa/frontend_dist/assets/DomainsView-DasJ0NdZ.css +0 -1
  251. modoboa/frontend_dist/assets/EmailField-C8umy0EU.js +0 -1
  252. modoboa/frontend_dist/assets/EmailView-ki7uEQPD.js +0 -1
  253. modoboa/frontend_dist/assets/EmptyLayout-DaA1XH9n.js +0 -1
  254. modoboa/frontend_dist/assets/FiltersView-FYFZxG4B.js +0 -1
  255. modoboa/frontend_dist/assets/ForwardEmailView-cUbnSYCF.js +0 -1
  256. modoboa/frontend_dist/assets/IdentitiesView-njNo8N5n.js +0 -1
  257. modoboa/frontend_dist/assets/InformationView-D1h38POt.js +0 -1
  258. modoboa/frontend_dist/assets/InformationView-U5Ww-Sx1.css +0 -1
  259. modoboa/frontend_dist/assets/MailboxView-IlrLWm_H.js +0 -1
  260. modoboa/frontend_dist/assets/MenuItems-BAtHWzAE.js +0 -1
  261. modoboa/frontend_dist/assets/MessagesView-OSpjixFq.js +0 -1
  262. modoboa/frontend_dist/assets/MigrationsView-DKNOsVzF.js +0 -1
  263. modoboa/frontend_dist/assets/ParametersView-C4bXASiq.js +0 -1
  264. modoboa/frontend_dist/assets/ParametersView-CYXgNmc1.js +0 -1
  265. modoboa/frontend_dist/assets/ProviderEditView-CyxCWTST.js +0 -1
  266. modoboa/frontend_dist/assets/ProviderGeneralForm-BYPjNHqB.js +0 -1
  267. modoboa/frontend_dist/assets/ProvidersView-CxrMkRyk.js +0 -1
  268. modoboa/frontend_dist/assets/ReplyEmailView-Dkw9-N26.js +0 -1
  269. modoboa/frontend_dist/assets/ResourcesForm-CuUvrOdY.js +0 -1
  270. modoboa/frontend_dist/assets/StatisticsView-BN7QsZMT.js +0 -1
  271. modoboa/frontend_dist/assets/TimeSerieChart-BMN8BeFZ.js +0 -1
  272. modoboa/frontend_dist/assets/UserLayout-B6-JQg4F.js +0 -1
  273. modoboa/frontend_dist/assets/VAlert-DIQTrRif.js +0 -1
  274. modoboa/frontend_dist/assets/VApp-CpkYA7js.js +0 -1
  275. modoboa/frontend_dist/assets/VAutocomplete-C4IpXyl8.js +0 -1
  276. modoboa/frontend_dist/assets/VAvatar-Lpb-Dion.js +0 -1
  277. modoboa/frontend_dist/assets/VCard-er_isjE_.js +0 -1
  278. modoboa/frontend_dist/assets/VCheckbox-D-u8JXP1.js +0 -1
  279. modoboa/frontend_dist/assets/VChip-B4iSpj8_.js +0 -1
  280. modoboa/frontend_dist/assets/VColorPicker-BAjGDsXv.js +0 -1
  281. modoboa/frontend_dist/assets/VDataTable-4JRjbtgF.js +0 -1
  282. modoboa/frontend_dist/assets/VDataTableServer-tIDT1m3-.js +0 -1
  283. modoboa/frontend_dist/assets/VDataTableVirtual-BlnO18u_.js +0 -1
  284. modoboa/frontend_dist/assets/VExpansionPanels-CwGtXDhr.js +0 -1
  285. modoboa/frontend_dist/assets/VFileInput-D1_7ZkO_.js +0 -1
  286. modoboa/frontend_dist/assets/VForm-DAkW4nfy.js +0 -1
  287. modoboa/frontend_dist/assets/VMenu-BPFJwj2f.js +0 -1
  288. modoboa/frontend_dist/assets/VPicker-CfT82M8N.js +0 -1
  289. modoboa/frontend_dist/assets/VProgressCircular-w75-3ogi.js +0 -1
  290. modoboa/frontend_dist/assets/VRadioGroup-0j6DNC_k.js +0 -1
  291. modoboa/frontend_dist/assets/VSelect-Cs4ARbAS.js +0 -1
  292. modoboa/frontend_dist/assets/VSelectionControl-Dg-XyRRS.js +0 -1
  293. modoboa/frontend_dist/assets/VSheet-Btq_Mu4s.js +0 -1
  294. modoboa/frontend_dist/assets/VSpacer-C7xukQmu.js +0 -1
  295. modoboa/frontend_dist/assets/VSwitch-Cs1NQrmk.js +0 -1
  296. modoboa/frontend_dist/assets/VTable-CNz2SGk4.js +0 -1
  297. modoboa/frontend_dist/assets/VTabs-B1fyVn4M.js +0 -1
  298. modoboa/frontend_dist/assets/VTextField-BdyvgvkG.js +0 -1
  299. modoboa/frontend_dist/assets/VTextField-C-J20yj_.css +0 -1
  300. modoboa/frontend_dist/assets/VTextarea-DnOMpe0Q.js +0 -1
  301. modoboa/frontend_dist/assets/VToolbar-BiCiBxBJ.js +0 -1
  302. modoboa/frontend_dist/assets/VWindowItem-ChWm_kz3.js +0 -1
  303. modoboa/frontend_dist/assets/WebmailLayout-o4uEkp9e.js +0 -1
  304. modoboa/frontend_dist/assets/forwardRefs-Dvjn_Xq4.js +0 -1
  305. modoboa/frontend_dist/assets/global.store-BaiD63EN.js +0 -1
  306. modoboa/frontend_dist/assets/index-I1VDlN4g.js +0 -984
  307. modoboa/frontend_dist/assets/layout-D8ZJPiJ_.js +0 -1
  308. modoboa/frontend_dist/assets/permissions-CITHLHVg.js +0 -1
  309. modoboa/frontend_dist/assets/transports-Dz7c6kIy.js +0 -1
  310. {modoboa-2.4.11.data → modoboa-2.5.0.data}/scripts/modoboa-admin.py +0 -0
  311. {modoboa-2.4.11.dist-info → modoboa-2.5.0.dist-info}/WHEEL +0 -0
  312. {modoboa-2.4.11.dist-info → modoboa-2.5.0.dist-info}/entry_points.txt +0 -0
  313. {modoboa-2.4.11.dist-info → modoboa-2.5.0.dist-info}/licenses/LICENSE +0 -0
  314. {modoboa-2.4.11.dist-info → modoboa-2.5.0.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)