modoboa 2.4.10__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 (331) hide show
  1. modoboa/admin/api/v1/serializers.py +1 -0
  2. modoboa/admin/api/v2/serializers.py +15 -1
  3. modoboa/admin/api/v2/tests.py +11 -1
  4. modoboa/admin/lib.py +1 -1
  5. modoboa/admin/models/mailbox.py +15 -13
  6. modoboa/admin/models/mxrecord.py +4 -0
  7. modoboa/admin/tests/test_import_.py +11 -11
  8. modoboa/amavis/__init__.py +3 -0
  9. modoboa/amavis/app_settings.py +276 -0
  10. modoboa/amavis/apps.py +18 -0
  11. modoboa/amavis/checks/__init__.py +2 -0
  12. modoboa/amavis/checks/settings_checks.py +59 -0
  13. modoboa/amavis/dbrouter.py +35 -0
  14. modoboa/amavis/factories.py +164 -0
  15. modoboa/amavis/handlers.py +146 -0
  16. modoboa/amavis/lib.py +381 -0
  17. modoboa/amavis/management/__init__.py +0 -0
  18. modoboa/amavis/management/commands/__init__.py +0 -0
  19. modoboa/amavis/management/commands/amnotify.py +99 -0
  20. modoboa/amavis/management/commands/qcleanup.py +84 -0
  21. modoboa/amavis/migrations/0001_initial.py +340 -0
  22. modoboa/amavis/migrations/__init__.py +0 -0
  23. modoboa/amavis/models.py +226 -0
  24. modoboa/amavis/serializers.py +139 -0
  25. modoboa/amavis/sql_connector.py +240 -0
  26. modoboa/amavis/sql_email.py +66 -0
  27. modoboa/amavis/tasks.py +33 -0
  28. modoboa/amavis/templates/amavis/notifications/pending_requests.html +16 -0
  29. modoboa/amavis/tests/__init__.py +0 -0
  30. modoboa/amavis/tests/sa-learn +3 -0
  31. modoboa/amavis/tests/sample_messages/quarantined-input.txt +80 -0
  32. modoboa/amavis/tests/sample_messages/quarantined-output-plain_nolinks.txt +17 -0
  33. modoboa/amavis/tests/spamc +3 -0
  34. modoboa/amavis/tests/test_checks.py +25 -0
  35. modoboa/amavis/tests/test_handlers.py +214 -0
  36. modoboa/amavis/tests/test_lib.py +90 -0
  37. modoboa/amavis/tests/test_management_commands.py +45 -0
  38. modoboa/amavis/tests/test_sql_email.py +67 -0
  39. modoboa/amavis/tests/test_utils.py +19 -0
  40. modoboa/amavis/tests/test_viewsets.py +319 -0
  41. modoboa/amavis/urls.py +11 -0
  42. modoboa/amavis/utils.py +105 -0
  43. modoboa/amavis/viewsets.py +265 -0
  44. modoboa/core/api/v1/serializers.py +7 -5
  45. modoboa/core/api/v2/serializers.py +13 -2
  46. modoboa/core/api/v2/tests.py +34 -4
  47. modoboa/core/api/v2/urls.py +10 -5
  48. modoboa/core/api/v2/views.py +23 -2
  49. modoboa/core/api/v2/viewsets.py +24 -3
  50. modoboa/core/app_settings.py +11 -0
  51. modoboa/core/fido2_auth.py +1 -2
  52. modoboa/core/handlers.py +6 -2
  53. modoboa/core/management/commands/add_allowed_hosts.py +33 -0
  54. modoboa/core/management/commands/cleanlogs.py +9 -0
  55. modoboa/core/management/commands/load_initial_data.py +10 -0
  56. modoboa/core/migrations/0025_rename_user_email_is_active_core_user_email_c0c03f_idx.py +23 -5
  57. modoboa/core/tests/test_core.py +29 -0
  58. modoboa/core/utils.py +6 -0
  59. modoboa/dnstools/api/v2/serializers.py +9 -11
  60. modoboa/frontend_dist/assets/AccountAliasForm-BuSy_1n9.js +1 -0
  61. modoboa/frontend_dist/assets/AccountEditView-qdJmLM_e.js +1 -0
  62. modoboa/frontend_dist/assets/AccountLayout-DrN7vHsX.js +1 -0
  63. modoboa/frontend_dist/assets/AccountPasswordSubForm-DZGt_Xgq.js +1 -0
  64. modoboa/frontend_dist/assets/AccountView-CO65y0vZ.js +1 -0
  65. modoboa/frontend_dist/assets/AddressBook-BZNUlhek.js +1 -0
  66. modoboa/frontend_dist/assets/AdminLayout-CTNhuwTw.js +1 -0
  67. modoboa/frontend_dist/assets/AlarmsView-9yKGbmkC.css +1 -0
  68. modoboa/frontend_dist/assets/AlarmsView-DN_JIw9g.js +1 -0
  69. modoboa/frontend_dist/assets/AliasEditView-DjpPUTp9.js +1 -0
  70. modoboa/frontend_dist/assets/{AliasRecipientForm-DVZXWaUX.js → AliasRecipientForm-B1Y8wFdP.js} +1 -1
  71. modoboa/frontend_dist/assets/AliasView-GOJ5lyQH.js +1 -0
  72. modoboa/frontend_dist/assets/AuditTrailView-fbXmq70e.js +1 -0
  73. modoboa/frontend_dist/assets/CalendarView-LlQQNEPL.js +1 -0
  74. modoboa/frontend_dist/assets/{ChoiceField-7eU7c_rI.js → ChoiceField-B3ReQHVe.js} +1 -1
  75. modoboa/frontend_dist/assets/ComposeEmailForm-Bs1fZXAL.js +1 -0
  76. modoboa/frontend_dist/assets/ComposeEmailView-s3LMl3pO.js +1 -0
  77. modoboa/frontend_dist/assets/ConfirmDialog-DY_kUHLG.js +1 -0
  78. modoboa/frontend_dist/assets/{ConnectedLayout-BaJZ3BeR.css → ConnectedLayout-Bxh21hcH.css} +1 -1
  79. modoboa/frontend_dist/assets/ConnectedLayout-UWjiYBNw.js +1 -0
  80. modoboa/frontend_dist/assets/CreationForm-ORg3fazt.js +1 -0
  81. modoboa/frontend_dist/assets/DashboardView-Dplk9itS.js +1 -0
  82. modoboa/frontend_dist/assets/{DashboardView-BLlMi6Qb.css → DashboardView-gwwVAPvt.css} +1 -1
  83. modoboa/frontend_dist/assets/DomainAdminList-DVn9x0rB.js +1 -0
  84. modoboa/frontend_dist/assets/DomainEditView-nAoL64D_.js +1 -0
  85. modoboa/frontend_dist/assets/{DomainTransportForm-DPnPGBOp.js → DomainTransportForm-CA-DNUxX.js} +1 -1
  86. modoboa/frontend_dist/assets/{DomainView-BDKoBFYr.css → DomainView-CCLYXPHx.css} +1 -1
  87. modoboa/frontend_dist/assets/DomainView-CdXPpwJG.js +5 -0
  88. modoboa/frontend_dist/assets/DomainsView-B_59gowf.js +1 -0
  89. modoboa/frontend_dist/assets/DomainsView-DZ-ss9bI.css +1 -0
  90. modoboa/frontend_dist/assets/EmailField-CwcwI5xW.js +1 -0
  91. modoboa/frontend_dist/assets/EmailView-BshxcfAK.js +1 -0
  92. modoboa/frontend_dist/assets/EmptyLayout-DFfhnhLi.js +1 -0
  93. modoboa/frontend_dist/assets/FiltersView-Cf20MSTK.js +1 -0
  94. modoboa/frontend_dist/assets/ForwardEmailView-CZG062os.js +1 -0
  95. modoboa/frontend_dist/assets/{HtmlEditor-uM4AtIGi.js → HtmlEditor-Bh4c689R.js} +1 -1
  96. modoboa/frontend_dist/assets/IdentitiesView-BXAuU1YX.js +1 -0
  97. modoboa/frontend_dist/assets/{IdentitiesView-jmuItyMZ.css → IdentitiesView-DPrrRMS5.css} +1 -1
  98. modoboa/frontend_dist/assets/InformationView-C9vvvQhJ.css +1 -0
  99. modoboa/frontend_dist/assets/InformationView-Cn5FZW7H.js +1 -0
  100. modoboa/frontend_dist/assets/{LoadingData-CVD2Aen8.js → LoadingData-CdVvm4FI.js} +1 -1
  101. modoboa/frontend_dist/assets/{LoginCallbackView-sWzBke1g.js → LoginCallbackView-B9hAH4MI.js} +1 -1
  102. modoboa/frontend_dist/assets/{LoginView-wmN73W0f.js → LoginView-tHIR4Adc.js} +1 -1
  103. modoboa/frontend_dist/assets/MailboxView-Bugu2vhg.js +1 -0
  104. modoboa/frontend_dist/assets/MenuItems-PXjiG-fs.js +1 -0
  105. modoboa/frontend_dist/assets/MessageView-Cy4STShm.js +1 -0
  106. modoboa/frontend_dist/assets/MessagesView-DdkuEgfX.js +1 -0
  107. modoboa/frontend_dist/assets/MigrationsView-CidSEjCF.js +1 -0
  108. modoboa/frontend_dist/assets/{ParametersForm-BCeQljir.js → ParametersForm-CAv4SH-E.js} +1 -1
  109. modoboa/frontend_dist/assets/ParametersView-CX7Ffemw.js +1 -0
  110. modoboa/frontend_dist/assets/ParametersView-CrbNcmV3.js +1 -0
  111. modoboa/frontend_dist/assets/ProviderEditView-CrltAQXl.js +1 -0
  112. modoboa/frontend_dist/assets/ProviderGeneralForm-BYAzVnXM.js +1 -0
  113. modoboa/frontend_dist/assets/ProvidersView-osjIY4Ex.js +1 -0
  114. modoboa/frontend_dist/assets/QuarantineLayout-B8EcU9vS.js +1 -0
  115. modoboa/frontend_dist/assets/QuarantineView-D4gOE4EQ.css +1 -0
  116. modoboa/frontend_dist/assets/QuarantineView-D8Qg0MXA.js +1 -0
  117. modoboa/frontend_dist/assets/ReplyEmailView-BABPqWhd.js +1 -0
  118. modoboa/frontend_dist/assets/ResourcesForm-OaqdRYVs.js +1 -0
  119. modoboa/frontend_dist/assets/SelfServiceLayout-d277YTGR.js +1 -0
  120. modoboa/frontend_dist/assets/SettingsView-9iNcDhkI.js +6 -0
  121. modoboa/frontend_dist/assets/StatisticsView-cHsPyGkL.js +1 -0
  122. modoboa/frontend_dist/assets/TimeSerieChart--V83dcJ9.js +1 -0
  123. modoboa/frontend_dist/assets/UserLayout-B3sBiTcZ.js +1 -0
  124. modoboa/frontend_dist/assets/VAlert-BuaaYN2h.js +1 -0
  125. modoboa/frontend_dist/assets/VApp-CKP-6zGP.js +1 -0
  126. modoboa/frontend_dist/assets/VAutocomplete-Dwv6_Rzq.js +1 -0
  127. modoboa/frontend_dist/assets/VAvatar-Cmga0vj6.js +1 -0
  128. modoboa/frontend_dist/assets/VBadge-BQrRJ9S0.css +1 -0
  129. modoboa/frontend_dist/assets/VBadge-CixeK87a.js +1 -0
  130. modoboa/frontend_dist/assets/VCard-CxH9DWoK.js +1 -0
  131. modoboa/frontend_dist/assets/VCheckbox-62GOpvvP.js +1 -0
  132. modoboa/frontend_dist/assets/{VCheckboxBtn-j7di4leN.js → VCheckboxBtn-DMoNtKT8.js} +1 -1
  133. modoboa/frontend_dist/assets/VChip-D_styETR.js +1 -0
  134. modoboa/frontend_dist/assets/VColorPicker-BHscBGQV.js +1 -0
  135. modoboa/frontend_dist/assets/VContainer-B46caNs1.js +1 -0
  136. modoboa/frontend_dist/assets/VDataTable-Bh8NbVSx.js +1 -0
  137. modoboa/frontend_dist/assets/VDataTableServer-BDR5hOmo.js +1 -0
  138. modoboa/frontend_dist/assets/VDataTableVirtual-BOQlNtIG.js +1 -0
  139. modoboa/frontend_dist/assets/{VDialog-CZqM2Ofu.js → VDialog-BcTg7w6P.js} +1 -1
  140. modoboa/frontend_dist/assets/VExpansionPanels-BmH5Jl2Z.js +1 -0
  141. modoboa/frontend_dist/assets/VFileInput-BC4yAygd.js +1 -0
  142. modoboa/frontend_dist/assets/VForm-D5iPGkde.js +1 -0
  143. modoboa/frontend_dist/assets/VInput-CcxkaOXT.css +1 -0
  144. modoboa/frontend_dist/assets/VInput-CoDJzvaW.js +1 -0
  145. modoboa/frontend_dist/assets/VMenu-gUG70-zD.js +1 -0
  146. modoboa/frontend_dist/assets/VPicker-BXuKT3zB.js +1 -0
  147. modoboa/frontend_dist/assets/VProgressCircular-BtOPiGCg.js +1 -0
  148. modoboa/frontend_dist/assets/VRadioGroup-DIFZKSn-.js +1 -0
  149. modoboa/frontend_dist/assets/{VRow-C_Ydf6yr.js → VRow-ozg66L7j.js} +1 -1
  150. modoboa/frontend_dist/assets/VSelect-C3RjAa45.js +1 -0
  151. modoboa/frontend_dist/assets/VSelectionControl-zyz-fJvC.js +1 -0
  152. modoboa/frontend_dist/assets/VSheet-BNx2X4Mk.js +1 -0
  153. modoboa/frontend_dist/assets/VSpacer-DinPiXs9.js +1 -0
  154. modoboa/frontend_dist/assets/VSwitch-DwxdeAEq.js +1 -0
  155. modoboa/frontend_dist/assets/VTable-DaLxa4FO.js +1 -0
  156. modoboa/frontend_dist/assets/VTabs-BP0Hgsgm.js +1 -0
  157. modoboa/frontend_dist/assets/VTextField-BzBVKKob.css +1 -0
  158. modoboa/frontend_dist/assets/VTextField-XoGTj1KG.js +1 -0
  159. modoboa/frontend_dist/assets/VTextarea-wBlRMIv_.js +1 -0
  160. modoboa/frontend_dist/assets/VToolbar-CFZfqeOr.js +1 -0
  161. modoboa/frontend_dist/assets/VWindowItem-BB7ETW3b.js +1 -0
  162. modoboa/frontend_dist/assets/WebmailLayout-_Hk1XhVq.js +1 -0
  163. modoboa/frontend_dist/assets/accounts-DUzbx6k8.js +1 -0
  164. modoboa/frontend_dist/assets/admin-DewTk2H8.js +1 -0
  165. modoboa/frontend_dist/assets/{aliases-c3n-dCV_.js → aliases-4sXmjwXp.js} +1 -1
  166. modoboa/frontend_dist/assets/amavis-CC0li7_T.js +1 -0
  167. modoboa/frontend_dist/assets/amavis-DK8SHE6o.js +1 -0
  168. modoboa/frontend_dist/assets/{contacts-Bqckz8sr.js → contacts-BjghrPqZ.js} +1 -1
  169. modoboa/frontend_dist/assets/{domains-nBMR-fRf.js → domains-BSawReeu.js} +1 -1
  170. modoboa/frontend_dist/assets/{domains.store-ChZgLcqP.js → domains.store-D-vWCEIK.js} +1 -1
  171. modoboa/frontend_dist/assets/{filter-D7NrAf6a.js → filter-C82FUCw_.js} +1 -1
  172. modoboa/frontend_dist/assets/forwardRefs-cvcnlhoK.js +1 -0
  173. modoboa/frontend_dist/assets/global.store-DbkcI5o2.js +1 -0
  174. modoboa/frontend_dist/assets/{importExport-Dn9vYw7T.js → importExport-DzoL4Mvc.js} +1 -1
  175. modoboa/frontend_dist/assets/index-BImkz5Jx.js +984 -0
  176. modoboa/frontend_dist/assets/index-DuzUMVLM.js +1 -0
  177. modoboa/frontend_dist/assets/layout-C5FyYCHK.js +1 -0
  178. modoboa/frontend_dist/assets/{layout.store-BxBoBlgf.js → layout.store-NXWtFIwL.js} +1 -1
  179. modoboa/frontend_dist/assets/logos-BswdveCV.js +1 -0
  180. modoboa/frontend_dist/assets/{logs-DrTzylW7.js → logs-6CbtfaZS.js} +1 -1
  181. modoboa/frontend_dist/assets/{parameters-Lgiqp7aw.js → parameters-aSQiR7kN.js} +1 -1
  182. modoboa/frontend_dist/assets/{parameters.store-C9k9DuTj.js → parameters.store-CzQqVatx.js} +1 -1
  183. modoboa/frontend_dist/assets/permissions-DNoefz-n.js +1 -0
  184. modoboa/frontend_dist/assets/{ssrBoot-BsxW6uW4.js → ssrBoot-CKUX4kcb.js} +1 -1
  185. modoboa/frontend_dist/assets/{tag-CFK9dzMJ.js → tag-B_yWNNJD.js} +1 -1
  186. modoboa/frontend_dist/assets/transports-BDNB9wR5.js +1 -0
  187. modoboa/frontend_dist/assets/{webmail-KrD8ZhNM.js → webmail-CdU6CD9b.js} +1 -1
  188. modoboa/frontend_dist/index.html +1 -1
  189. modoboa/lib/email_utils.py +2 -2
  190. modoboa/lib/permissions.py +7 -0
  191. modoboa/lib/redis.py +1 -5
  192. modoboa/lib/test_runners.py +29 -0
  193. modoboa/lib/tests/__init__.py +5 -1
  194. modoboa/locale/br/LC_MESSAGES/django.po +87 -75
  195. modoboa/locale/cs/LC_MESSAGES/django.po +82 -74
  196. modoboa/locale/cs_CZ/LC_MESSAGES/django.po +145 -121
  197. modoboa/locale/de/LC_MESSAGES/django.mo +0 -0
  198. modoboa/locale/de/LC_MESSAGES/django.po +339 -651
  199. modoboa/locale/de_DE/LC_MESSAGES/django.po +87 -75
  200. modoboa/locale/el_GR/LC_MESSAGES/django.po +160 -135
  201. modoboa/locale/en/LC_MESSAGES/django.po +82 -74
  202. modoboa/locale/es/LC_MESSAGES/django.po +158 -131
  203. modoboa/locale/es_MX/LC_MESSAGES/django.po +82 -74
  204. modoboa/locale/fi/LC_MESSAGES/django.po +87 -75
  205. modoboa/locale/fr/LC_MESSAGES/django.mo +0 -0
  206. modoboa/locale/fr/LC_MESSAGES/django.po +469 -201
  207. modoboa/locale/hu/LC_MESSAGES/django.po +82 -74
  208. modoboa/locale/it/LC_MESSAGES/django.mo +0 -0
  209. modoboa/locale/it/LC_MESSAGES/django.po +148 -122
  210. modoboa/locale/ja_JP/LC_MESSAGES/django.mo +0 -0
  211. modoboa/locale/ja_JP/LC_MESSAGES/django.po +201 -334
  212. modoboa/locale/ka/LC_MESSAGES/django.po +82 -74
  213. modoboa/locale/nl_NL/LC_MESSAGES/django.po +160 -132
  214. modoboa/locale/no/LC_MESSAGES/django.po +82 -74
  215. modoboa/locale/pl_PL/LC_MESSAGES/django.mo +0 -0
  216. modoboa/locale/pl_PL/LC_MESSAGES/django.po +172 -149
  217. modoboa/locale/pt_BR/LC_MESSAGES/django.mo +0 -0
  218. modoboa/locale/pt_BR/LC_MESSAGES/django.po +172 -144
  219. modoboa/locale/pt_PT/LC_MESSAGES/django.po +135 -112
  220. modoboa/locale/ro_RO/LC_MESSAGES/django.po +87 -75
  221. modoboa/locale/ru/LC_MESSAGES/django.po +142 -118
  222. modoboa/locale/si/LC_MESSAGES/django.po +82 -74
  223. modoboa/locale/sk/LC_MESSAGES/django.po +82 -74
  224. modoboa/locale/sk_SK/LC_MESSAGES/django.po +84 -76
  225. modoboa/locale/sl_SI/LC_MESSAGES/django.po +90 -76
  226. modoboa/locale/sv/LC_MESSAGES/django.mo +0 -0
  227. modoboa/locale/sv/LC_MESSAGES/django.po +172 -139
  228. modoboa/locale/tr/LC_MESSAGES/django.po +87 -75
  229. modoboa/locale/tr_TR/LC_MESSAGES/django.po +84 -74
  230. modoboa/locale/uk/LC_MESSAGES/django.po +82 -74
  231. modoboa/locale/zh/LC_MESSAGES/django.po +82 -74
  232. modoboa/locale/zh_CN/LC_MESSAGES/django.po +82 -74
  233. modoboa/locale/zh_TW/LC_MESSAGES/django.po +87 -75
  234. modoboa/parameters/api/v2/tests.py +2 -2
  235. modoboa/parameters/api/v2/viewsets.py +2 -0
  236. modoboa/policyd/tests.py +2 -0
  237. modoboa/urls_api_v2.py +6 -0
  238. {modoboa-2.4.10.dist-info → modoboa-2.5.0.dist-info}/METADATA +6 -4
  239. {modoboa-2.4.10.dist-info → modoboa-2.5.0.dist-info}/RECORD +244 -193
  240. modoboa/frontend_dist/assets/AccountAliasForm-DVXatAhB.js +0 -1
  241. modoboa/frontend_dist/assets/AccountEditView-DmvQjxpx.js +0 -1
  242. modoboa/frontend_dist/assets/AccountLayout-OGtZvlHR.js +0 -1
  243. modoboa/frontend_dist/assets/AccountPasswordSubForm-g3IEGrgM.js +0 -1
  244. modoboa/frontend_dist/assets/AccountView-DsxYqr3k.js +0 -1
  245. modoboa/frontend_dist/assets/AddressBook-3RoKiKon.js +0 -1
  246. modoboa/frontend_dist/assets/AdminLayout-CWfn8zaQ.js +0 -1
  247. modoboa/frontend_dist/assets/AlarmsView-Bheey-gp.css +0 -1
  248. modoboa/frontend_dist/assets/AlarmsView-D3Mh8ntf.js +0 -1
  249. modoboa/frontend_dist/assets/AliasEditView-C15eUZ11.js +0 -1
  250. modoboa/frontend_dist/assets/AliasView-Cyvc5vMb.js +0 -1
  251. modoboa/frontend_dist/assets/AuditTrailView-LI2XuLLn.js +0 -1
  252. modoboa/frontend_dist/assets/CalendarView-BpOlPh3f.js +0 -1
  253. modoboa/frontend_dist/assets/ComposeEmailForm-CNfI7ept.js +0 -1
  254. modoboa/frontend_dist/assets/ComposeEmailView-B866Xsrc.js +0 -1
  255. modoboa/frontend_dist/assets/ConfirmDialog-BvqxQsD1.js +0 -1
  256. modoboa/frontend_dist/assets/ConnectedLayout-BQ3ug6Jh.js +0 -1
  257. modoboa/frontend_dist/assets/CreationForm-C9Kh05ax.js +0 -1
  258. modoboa/frontend_dist/assets/DashboardView-Cw-gIcuB.js +0 -1
  259. modoboa/frontend_dist/assets/DomainAdminList-CsWUNKVk.js +0 -1
  260. modoboa/frontend_dist/assets/DomainEditView-DocxeOeW.js +0 -1
  261. modoboa/frontend_dist/assets/DomainView-Djc_0PsF.js +0 -5
  262. modoboa/frontend_dist/assets/DomainsView-B-Lxru7P.js +0 -1
  263. modoboa/frontend_dist/assets/DomainsView-DasJ0NdZ.css +0 -1
  264. modoboa/frontend_dist/assets/EmailField-BEKxuYni.js +0 -1
  265. modoboa/frontend_dist/assets/EmailView-BsR1Wes5.js +0 -1
  266. modoboa/frontend_dist/assets/EmptyLayout-BzPFOeLU.js +0 -1
  267. modoboa/frontend_dist/assets/FiltersView-5rmpC5cC.js +0 -1
  268. modoboa/frontend_dist/assets/ForwardEmailView-D7MbetcT.js +0 -1
  269. modoboa/frontend_dist/assets/IdentitiesView-BqjD9Lue.js +0 -1
  270. modoboa/frontend_dist/assets/InformationView-CghcvPn2.js +0 -1
  271. modoboa/frontend_dist/assets/InformationView-U5Ww-Sx1.css +0 -1
  272. modoboa/frontend_dist/assets/MailboxView-T_p-_ZtJ.js +0 -1
  273. modoboa/frontend_dist/assets/MenuItems-kHCMzR5E.js +0 -1
  274. modoboa/frontend_dist/assets/MessagesView-Cerv3xsy.js +0 -1
  275. modoboa/frontend_dist/assets/MigrationsView-7kjqPyYU.js +0 -1
  276. modoboa/frontend_dist/assets/ParametersView-DspBxVMV.js +0 -1
  277. modoboa/frontend_dist/assets/ParametersView-Dy0H5ep1.js +0 -1
  278. modoboa/frontend_dist/assets/ProviderEditView-DDLMOylC.js +0 -1
  279. modoboa/frontend_dist/assets/ProviderGeneralForm-BwOSKNHK.js +0 -1
  280. modoboa/frontend_dist/assets/ProvidersView-C99UD0WB.js +0 -1
  281. modoboa/frontend_dist/assets/ReplyEmailView-DAPBHldd.js +0 -1
  282. modoboa/frontend_dist/assets/ResourcesForm-D87PHeH0.js +0 -1
  283. modoboa/frontend_dist/assets/SettingsView-OxDo9wNd.js +0 -6
  284. modoboa/frontend_dist/assets/StatisticsView-CM__Eqku.js +0 -1
  285. modoboa/frontend_dist/assets/TimeSerieChart-C6j0uYR_.js +0 -1
  286. modoboa/frontend_dist/assets/UserLayout-CckCGnPS.js +0 -1
  287. modoboa/frontend_dist/assets/VAlert-B7mzOJIO.js +0 -1
  288. modoboa/frontend_dist/assets/VApp-BKxnjOoi.js +0 -1
  289. modoboa/frontend_dist/assets/VAutocomplete-Cc4_tcl1.js +0 -1
  290. modoboa/frontend_dist/assets/VAvatar-BAgTUIvX.js +0 -1
  291. modoboa/frontend_dist/assets/VCard-DFWiFORP.js +0 -1
  292. modoboa/frontend_dist/assets/VCheckbox-CKsH_vq3.js +0 -1
  293. modoboa/frontend_dist/assets/VChip-nZ0uhY7t.js +0 -1
  294. modoboa/frontend_dist/assets/VColorPicker-Os2aeP6J.js +0 -1
  295. modoboa/frontend_dist/assets/VContainer-Btam4lk2.js +0 -1
  296. modoboa/frontend_dist/assets/VDataTable-D_0_xJTl.js +0 -1
  297. modoboa/frontend_dist/assets/VDataTableServer-BWTt4Mzi.js +0 -1
  298. modoboa/frontend_dist/assets/VDataTableVirtual-BwVmkt4u.js +0 -1
  299. modoboa/frontend_dist/assets/VExpansionPanels-B5D6GOa3.js +0 -1
  300. modoboa/frontend_dist/assets/VFileInput-Cv9DIPki.js +0 -1
  301. modoboa/frontend_dist/assets/VForm-CpoZf60D.js +0 -1
  302. modoboa/frontend_dist/assets/VMenu-B_dVqOmo.js +0 -1
  303. modoboa/frontend_dist/assets/VPicker-CXkIGEze.js +0 -1
  304. modoboa/frontend_dist/assets/VProgressCircular-CrEXxs7k.js +0 -1
  305. modoboa/frontend_dist/assets/VRadioGroup-D8ypjYOO.js +0 -1
  306. modoboa/frontend_dist/assets/VSelect-CS51PDEt.js +0 -1
  307. modoboa/frontend_dist/assets/VSelectionControl-DiOqtY38.js +0 -1
  308. modoboa/frontend_dist/assets/VSheet-5VVWtHvs.js +0 -1
  309. modoboa/frontend_dist/assets/VSpacer-C6PZ3X24.js +0 -1
  310. modoboa/frontend_dist/assets/VSwitch-Chg5o-Cp.js +0 -1
  311. modoboa/frontend_dist/assets/VTable-KsiZ3cBz.js +0 -1
  312. modoboa/frontend_dist/assets/VTabs-tNrJIYO0.js +0 -1
  313. modoboa/frontend_dist/assets/VTextField-C-J20yj_.css +0 -1
  314. modoboa/frontend_dist/assets/VTextField-CLwRV0Cb.js +0 -1
  315. modoboa/frontend_dist/assets/VTextarea-Di8jbl8m.js +0 -1
  316. modoboa/frontend_dist/assets/VToolbar-CGwhgdmI.js +0 -1
  317. modoboa/frontend_dist/assets/VWindowItem-wWSFAGI-.js +0 -1
  318. modoboa/frontend_dist/assets/WebmailLayout-DsYThBrz.js +0 -1
  319. modoboa/frontend_dist/assets/admin-CJVLMHh9.js +0 -1
  320. modoboa/frontend_dist/assets/forwardRefs-Cpc3YYl6.js +0 -1
  321. modoboa/frontend_dist/assets/global.store-DUP26-A5.js +0 -1
  322. modoboa/frontend_dist/assets/index-DV9Li2cg.js +0 -852
  323. modoboa/frontend_dist/assets/index-DzL89N4E.js +0 -1
  324. modoboa/frontend_dist/assets/layout-CHO37cA6.js +0 -1
  325. modoboa/frontend_dist/assets/permissions-mL5hLHcW.js +0 -1
  326. modoboa/frontend_dist/assets/transports-TO08iTXJ.js +0 -1
  327. {modoboa-2.4.10.data → modoboa-2.5.0.data}/scripts/modoboa-admin.py +0 -0
  328. {modoboa-2.4.10.dist-info → modoboa-2.5.0.dist-info}/WHEEL +0 -0
  329. {modoboa-2.4.10.dist-info → modoboa-2.5.0.dist-info}/entry_points.txt +0 -0
  330. {modoboa-2.4.10.dist-info → modoboa-2.5.0.dist-info}/licenses/LICENSE +0 -0
  331. {modoboa-2.4.10.dist-info → modoboa-2.5.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,319 @@
1
+ import os
2
+ from unittest import mock
3
+
4
+ from rq import SimpleWorker
5
+
6
+ from django.core import mail
7
+ from django.core.management import call_command
8
+ from django.test import override_settings
9
+ from django.urls import reverse
10
+
11
+ import django_rq
12
+
13
+ from modoboa.admin import factories as admin_factories, models as admin_models
14
+ from modoboa.amavis import factories, models
15
+ from modoboa.amavis.utils import smart_str
16
+ from modoboa.core import models as core_models
17
+ from modoboa.lib.tests import ModoAPITestCase
18
+
19
+
20
+ AMAVIS_SETTINGS = {
21
+ "localpart_is_case_sensitive": False,
22
+ "recipient_delimiter": "+",
23
+ "max_attachment_size": 14,
24
+ "released_msgs_cleanup": False,
25
+ "am_pdp_mode": "unix",
26
+ "am_pdp_host": "localhost",
27
+ "am_pdp_port": 9998,
28
+ "user_can_release": False,
29
+ "self_service": False,
30
+ "manual_learning": True,
31
+ "sa_is_local": True,
32
+ }
33
+
34
+
35
+ class TestDataMixin:
36
+ """A mixin to provide test data."""
37
+
38
+ @classmethod
39
+ def setUpTestData(cls): # NOQA:N802
40
+ """Create some content."""
41
+ super().setUpTestData()
42
+ cls.msgrcpt = factories.create_spam("user@test.com")
43
+
44
+
45
+ @override_settings(SA_LOOKUP_PATH=(os.path.dirname(__file__),))
46
+ class QuarantineViewSetTestCase(TestDataMixin, ModoAPITestCase):
47
+
48
+ databases = "__all__"
49
+
50
+ @classmethod
51
+ def setUpTestData(cls): # NOQA:N802
52
+ """Create test data."""
53
+ super().setUpTestData()
54
+ admin_factories.populate_database()
55
+
56
+ def tearDown(self):
57
+ """Restore msgrcpt state."""
58
+ self.msgrcpt.rs = " "
59
+ self.msgrcpt.save(update_fields=["rs"])
60
+ self.set_global_parameter("domain_level_learning", False)
61
+ self.set_global_parameter("user_level_learning", False)
62
+
63
+ def test_list(self):
64
+ url = reverse("v2:amavis-quarantine-list")
65
+ resp = self.client.get(url)
66
+ self.assertEqual(resp.status_code, 200)
67
+ content = resp.json()
68
+ self.assertEqual(content["count"], 1)
69
+
70
+ resp = self.client.get(f"{url}?search=pouet&criteria=both")
71
+ content = resp.json()
72
+ self.assertEqual(content["count"], 0)
73
+
74
+ resp = self.client.get(f"{url}?msgtype=V")
75
+ content = resp.json()
76
+ self.assertEqual(content["count"], 0)
77
+ factories.create_virus("user@test.com")
78
+ resp = self.client.get(f"{url}?msgtype=V")
79
+ content = resp.json()
80
+ self.assertEqual(content["count"], 1)
81
+
82
+ def test_retrieve(self):
83
+ mail_id = smart_str(self.msgrcpt.mail.mail_id)
84
+ rcpt = smart_str(self.msgrcpt.rid.email)
85
+ url = reverse("v2:amavis-quarantine-detail", args=[mail_id])
86
+ resp = self.client.get(f"{url}?rcpt={rcpt}")
87
+ self.assertEqual(resp.status_code, 200)
88
+
89
+ user = core_models.User.objects.get(username="user@test.com")
90
+ self.client.force_authenticate(user)
91
+ resp = self.client.get(f"{url}?rcpt={rcpt}")
92
+ self.assertEqual(resp.status_code, 200)
93
+ self.msgrcpt.refresh_from_db()
94
+ self.assertEqual(self.msgrcpt.rs, "V")
95
+
96
+ def test_headers(self):
97
+ mail_id = smart_str(self.msgrcpt.mail.mail_id)
98
+ url = reverse("v2:amavis-quarantine-headers", args=[mail_id])
99
+ resp = self.client.get(url)
100
+ self.assertEqual(resp.status_code, 200)
101
+
102
+ def test_headers_selfservice(self):
103
+ self.client.logout()
104
+ mail_id = smart_str(self.msgrcpt.mail.mail_id)
105
+ url = reverse("v2:amavis-quarantine-headers", args=[mail_id])
106
+ url = f"{url}?secret_id={smart_str(self.msgrcpt.mail.secret_id)}"
107
+ response = self.client.get(url)
108
+ self.assertEqual(response.status_code, 403)
109
+
110
+ self.set_global_parameter("self_service", True)
111
+ response = self.client.get(url)
112
+ self.assertEqual(response.status_code, 403)
113
+ url = f"{url}&rcpt={smart_str(self.msgrcpt.rid.email)}"
114
+ response = self.client.get(url)
115
+ self.assertEqual(response.status_code, 200)
116
+
117
+ def test_delete(self):
118
+ mail_id = smart_str(self.msgrcpt.mail.mail_id)
119
+ url = reverse("v2:amavis-quarantine-delete", args=[mail_id])
120
+ data = {"mailid": mail_id, "rcpt": smart_str(self.msgrcpt.rid.email)}
121
+ response = self.client.post(url, data, format="json")
122
+ self.assertEqual(response.status_code, 204)
123
+ self.msgrcpt.refresh_from_db()
124
+ self.assertEqual(self.msgrcpt.rs, "D")
125
+
126
+ def test_delete_selfservice(self):
127
+ self.client.logout()
128
+ mail_id = smart_str(self.msgrcpt.mail.mail_id)
129
+ url = reverse("v2:amavis-quarantine-delete", args=[mail_id])
130
+ data = {"mailid": mail_id, "rcpt": smart_str(self.msgrcpt.rid.email)}
131
+ response = self.client.post(url, data, format="json")
132
+ self.assertEqual(response.status_code, 403)
133
+ self.set_global_parameter("self_service", True)
134
+ data["secret_id"] = smart_str(self.msgrcpt.mail.secret_id)
135
+ response = self.client.post(url, data, format="json")
136
+ self.assertEqual(response.status_code, 204)
137
+ self.msgrcpt.refresh_from_db()
138
+ self.assertEqual(self.msgrcpt.rs, "D")
139
+
140
+ def test_delete_selection(self):
141
+ mail_id = smart_str(self.msgrcpt.mail.mail_id)
142
+ url = reverse("v2:amavis-quarantine-delete-selection")
143
+ data = {
144
+ "selection": [
145
+ {"mailid": mail_id, "rcpt": smart_str(self.msgrcpt.rid.email)}
146
+ ]
147
+ }
148
+ response = self.client.post(url, data, format="json")
149
+ self.assertEqual(response.status_code, 204)
150
+ self.msgrcpt.refresh_from_db()
151
+ self.assertEqual(self.msgrcpt.rs, "D")
152
+
153
+ @mock.patch("socket.socket")
154
+ def test_release(self, mock_socket):
155
+ mock_socket.return_value.recv.return_value = b"250 1234 Ok\r\n"
156
+ mail_id = smart_str(self.msgrcpt.mail.mail_id)
157
+ url = reverse("v2:amavis-quarantine-release", args=[mail_id])
158
+ data = {"mailid": mail_id, "rcpt": smart_str(self.msgrcpt.rid.email)}
159
+ response = self.client.post(url, data, format="json")
160
+ self.assertEqual(response.status_code, 200)
161
+ self.msgrcpt.refresh_from_db()
162
+ self.assertEqual(self.msgrcpt.rs, "R")
163
+
164
+ @mock.patch("socket.socket")
165
+ def test_release_selfservice(self, mock_socket):
166
+ """Test release view."""
167
+ mock_socket.return_value.recv.return_value = b"250 1234 Ok\r\n"
168
+ self.client.logout()
169
+ mail_id = smart_str(self.msgrcpt.mail.mail_id)
170
+ url = reverse("v2:amavis-quarantine-release", args=[mail_id])
171
+ self.set_global_parameter("self_service", True)
172
+ # Missing rcpt -> fails
173
+ data = {"mailid": mail_id, "secret_id": "1234"}
174
+ resp = self.client.post(url, data, format="json")
175
+ self.assertEqual(resp.status_code, 403)
176
+ # Wrong secret_id -> fails
177
+ data["rcpt"] = smart_str(self.msgrcpt.rid.email)
178
+ resp = self.client.post(url, data, format="json")
179
+ self.assertEqual(resp.status_code, 403)
180
+ # Request mode
181
+ data["secret_id"] = smart_str(self.msgrcpt.mail.secret_id)
182
+ resp = self.client.post(url, data, format="json")
183
+ self.assertEqual(resp.status_code, 200)
184
+ self.msgrcpt.refresh_from_db()
185
+ self.assertEqual(self.msgrcpt.rs, "p")
186
+ # Direct release mode
187
+ self.set_global_parameter("user_can_release", True)
188
+ resp = self.client.post(url, data, format="json")
189
+ self.assertEqual(resp.status_code, 200)
190
+ self.msgrcpt.refresh_from_db()
191
+ self.assertEqual(self.msgrcpt.rs, "R")
192
+
193
+ def test_release_request(self):
194
+ """Test release request mode."""
195
+ user = core_models.User.objects.get(username="user@test.com")
196
+ self.client.force_authenticate(user)
197
+
198
+ mail_id = smart_str(self.msgrcpt.mail.mail_id)
199
+ url = reverse("v2:amavis-quarantine-release", args=[mail_id])
200
+ data = {"mailid": mail_id, "rcpt": smart_str(self.msgrcpt.rid.email)}
201
+ response = self.client.post(url, data, format="json")
202
+ self.assertEqual(response.status_code, 200)
203
+ self.msgrcpt.refresh_from_db()
204
+ self.assertEqual(self.msgrcpt.rs, "p")
205
+
206
+ # Send notification to admins
207
+ call_command("amnotify")
208
+ self.assertEqual(len(mail.outbox), 1)
209
+
210
+ def _test_mark_selection(self, action, status):
211
+ """Mark message common code."""
212
+ mail_id = smart_str(self.msgrcpt.mail.mail_id)
213
+ url = reverse("v2:amavis-quarantine-mark-selection")
214
+ data = {
215
+ "selection": [
216
+ {"mailid": mail_id, "rcpt": smart_str(self.msgrcpt.rid.email)}
217
+ ],
218
+ "type": action,
219
+ }
220
+ response = self.client.post(url, data, format="json")
221
+ self.assertEqual(response.status_code, 204)
222
+ queue = django_rq.get_queue("modoboa")
223
+ worker = SimpleWorker([queue], connection=queue.connection)
224
+ worker.work(burst=True)
225
+ self.msgrcpt.refresh_from_db()
226
+ self.assertEqual(self.msgrcpt.rs, status)
227
+
228
+ self.msgrcpt.rs = " "
229
+ self.msgrcpt.save(update_fields=["rs"])
230
+ self.set_global_parameter("sa_is_local", False)
231
+ response = self.client.post(url, data, format="json")
232
+ self.assertEqual(response.status_code, 204)
233
+ worker.work(burst=True)
234
+ self.msgrcpt.refresh_from_db()
235
+ self.assertEqual(self.msgrcpt.rs, status)
236
+
237
+ def test_mark_as_ham(self):
238
+ """Test mark_as_ham view."""
239
+ self._test_mark_selection("ham", "H")
240
+
241
+ # Check domain level learning
242
+ self.set_global_parameter("domain_level_learning", True)
243
+ data = {
244
+ "selection": [
245
+ {
246
+ "rcpt": smart_str(self.msgrcpt.rid.email),
247
+ "mailid": smart_str(self.msgrcpt.mail.mail_id),
248
+ },
249
+ ],
250
+ "type": "ham",
251
+ "database": "domain",
252
+ }
253
+ url = reverse("v2:amavis-quarantine-mark-selection")
254
+ response = self.client.post(url, data, format="json")
255
+ self.assertEqual(response.status_code, 204)
256
+
257
+ # Check user level learning
258
+ self.set_global_parameter("user_level_learning", True)
259
+ data["database"] = "user"
260
+ response = self.client.post(url, data, format="json")
261
+ self.assertEqual(response.status_code, 204)
262
+
263
+ def test_mark_as_spam(self):
264
+ """Test mark_as_spam view."""
265
+ self._test_mark_selection("spam", "S")
266
+
267
+ def test_manual_learning_as_user(self):
268
+ """Test learning when connected as a simple user."""
269
+ user = core_models.User.objects.get(username="user@test.com")
270
+ self.client.force_authenticate(user)
271
+ mail_id = smart_str(self.msgrcpt.mail.mail_id)
272
+ data = {
273
+ "selection": [
274
+ {"mailid": mail_id, "rcpt": smart_str(self.msgrcpt.rid.email)}
275
+ ],
276
+ "type": "ham",
277
+ }
278
+ url = reverse("v2:amavis-quarantine-mark-selection")
279
+ response = self.client.post(url, data, format="json")
280
+ self.assertEqual(response.status_code, 200)
281
+ self.assertEqual(response.json(), {"status": "ok"})
282
+ self.set_global_parameter("user_level_learning", True)
283
+ self._test_mark_selection("ham", "H")
284
+
285
+
286
+ class PolicyViewSetTestCase(ModoAPITestCase):
287
+
288
+ databases = "__all__"
289
+
290
+ @classmethod
291
+ def setUpTestData(cls): # NOQA:N802
292
+ """Create test data."""
293
+ super().setUpTestData()
294
+ admin_factories.populate_database()
295
+
296
+ def test_get_policy(self):
297
+ domain = admin_models.Domain.objects.get(name="test.com")
298
+ url = reverse("v2:amavis-policy-detail", args=[domain.pk])
299
+ resp = self.client.get(url)
300
+ self.assertEqual(resp.status_code, 200)
301
+
302
+ def test_update_policy(self):
303
+ domain = admin_models.Domain.objects.get(name="test.com")
304
+ data = {"bypass_virus_checks": "Y", "spam_tag2_level": 6}
305
+ url = reverse("v2:amavis-policy-detail", args=[domain.pk])
306
+ resp = self.client.patch(url, data, format="json")
307
+ self.assertEqual(resp.status_code, 200)
308
+ p = models.Policy.objects.get(users__email="@test.com")
309
+ self.assertEqual(p.bypass_virus_checks, data["bypass_virus_checks"])
310
+ self.assertEqual(p.spam_tag2_level, data["spam_tag2_level"])
311
+
312
+
313
+ class ParametersAPITestCase(ModoAPITestCase):
314
+
315
+ def test_update(self):
316
+ settings = AMAVIS_SETTINGS.copy()
317
+ url = reverse("v2:parameter-global-detail", args=["amavis"])
318
+ resp = self.client.put(url, settings, format="json")
319
+ self.assertEqual(resp.status_code, 200)
modoboa/amavis/urls.py ADDED
@@ -0,0 +1,11 @@
1
+ """Amavis urls."""
2
+
3
+ from rest_framework import routers
4
+
5
+ from modoboa.amavis import viewsets
6
+
7
+ router = routers.SimpleRouter()
8
+ router.register(r"quarantine", viewsets.QuarantineViewSet, basename="amavis-quarantine")
9
+ router.register(r"policies", viewsets.PolicyViewSet, basename="amavis-policy")
10
+
11
+ urlpatterns = router.urls
@@ -0,0 +1,105 @@
1
+ """A collection of utility functions for working with the Amavis database."""
2
+
3
+ import chardet
4
+
5
+ from django.conf import settings
6
+ from django.db.models.expressions import Func
7
+ from django.utils.encoding import (
8
+ smart_bytes as django_smart_bytes,
9
+ smart_str as django_smart_str,
10
+ )
11
+
12
+
13
+ """
14
+ Byte fields for text data are EVIL.
15
+
16
+ MySQL uses `varbyte` fields which mysqlclient client maps to `str` (Py2) or
17
+ `bytes` (Py3), Djangos smart_* functions work as expected.
18
+
19
+ PostgreSQL uses `bytea` fields which psycopg2 maps to `memoryview`,
20
+ Djangos smart_* functions don't work as expected, you must call `tobytes()` on
21
+ the memoryview for them to work.
22
+
23
+ For convenience use smart_bytes and smart_str from this file in modoboa.amavis
24
+ to avoid any headaches.
25
+ """
26
+
27
+
28
+ def smart_bytes(value, *args, **kwargs):
29
+ if isinstance(value, memoryview):
30
+ value = value.tobytes()
31
+ return django_smart_bytes(value, *args, **kwargs)
32
+
33
+
34
+ def smart_str(value, *args, **kwargs):
35
+ if isinstance(value, memoryview):
36
+ value = value.tobytes()
37
+ return django_smart_str(value, *args, **kwargs)
38
+
39
+
40
+ def fix_utf8_encoding(value):
41
+ """Fix utf-8 strings that contain utf-8 escaped characters.
42
+
43
+ msgs.from_addr and msgs.subject potentialy contain badly escaped utf-8
44
+ characters, this utility function fixes that and should be used anytime
45
+ these fields are accesses.
46
+
47
+ Didn't even know the raw_unicode_escape encoding existed :)
48
+ https://docs.python.org/3/library/codecs.html?highlight=raw_unicode_escape#python-specific-encodings
49
+ """
50
+ assert isinstance(value, str), "value should be of type str"
51
+
52
+ if len(value) == 0:
53
+ # short circuit for empty strings
54
+ return ""
55
+
56
+ bytes_value = value.encode("raw_unicode_escape")
57
+ try:
58
+ value = bytes_value.decode("utf-8")
59
+ except UnicodeDecodeError:
60
+ encoding = chardet.detect(bytes_value)
61
+ try:
62
+ value = bytes_value.decode(encoding["encoding"], "replace")
63
+ except (TypeError, UnicodeDecodeError):
64
+ # ??? use the original value, we've done our best to try and
65
+ # convert it to a clean utf-8 string.
66
+ pass
67
+
68
+ return value
69
+
70
+
71
+ class ConvertFrom(Func):
72
+ """Convert a binary value to a string.
73
+ Calls the database specific function to convert a binary value to a string
74
+ using the encoding set in AMAVIS_DEFAULT_DATABASE_ENCODING.
75
+ """
76
+
77
+ """PostgreSQL implementation.
78
+ See https://www.postgresql.org/docs/9.3/static/functions-string.html#FUNCTIONS-STRING-OTHER""" # NOQA:E501
79
+ function = "convert_from"
80
+ arity = 1
81
+ template = (
82
+ f"%(function)s(%(expressions)s, '{settings.AMAVIS_DEFAULT_DATABASE_ENCODING}')"
83
+ )
84
+
85
+ def as_mysql(self, compiler, connection):
86
+ """MySQL implementation.
87
+ See https://dev.mysql.com/doc/refman/5.5/en/cast-functions.html#function_convert
88
+ """ # NOQA:E501
89
+ return super().as_sql(
90
+ compiler,
91
+ connection,
92
+ function="CONVERT",
93
+ template=f"%(function)s(%(expressions)s USING {settings.AMAVIS_DEFAULT_DATABASE_ENCODING})",
94
+ arity=1,
95
+ )
96
+
97
+ def as_sqlite(self, compiler, connection):
98
+ """SQLite implementation.
99
+ SQLite has no equivilant function, just return the field."""
100
+ return super().as_sql(
101
+ compiler,
102
+ connection,
103
+ template="%(expressions)s",
104
+ arity=1,
105
+ )