Commit 18c60cef authored by Aurélien Bompard's avatar Aurélien Bompard

Use the new django-mailman3

parent 251d666b
......@@ -63,6 +63,7 @@ INSTALLED_APPS = (
'django.contrib.messages',
'django.contrib.staticfiles',
'postorius',
'django_mailman3',
'django_browserid',
)
......@@ -199,22 +200,27 @@ SERVER_EMAIL = 'root@localhost.local'
# EMAIL_CONFIRMATION_SUBJECT = 'Confirmation needed'
# You can enable logging by uncommenting the following lines
# LOGGING = {
# 'version': 1,
# 'disable_existing_loggers': False,
# 'handlers': {
# 'console': {
# 'class': 'logging.StreamHandler'
# },
# },
# 'loggers': {
# 'django': {
# 'handlers': ['console'],
# 'level': 'INFO',
# },
# 'django_browserid': {
# 'handlers': ['console'],
# 'level': 'DEBUG',
# },
# },
# }
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console': {
# 'class': 'logging.StreamHandler'
'class': 'logging.NullHandler'
},
},
'loggers': {
'django': {
'handlers': ['console'],
'level': 'INFO',
},
'postorius': {
'handlers': ['console'],
'level': 'INFO',
},
'django_browserid': {
'handlers': ['console'],
'level': 'INFO',
},
},
}
......@@ -36,6 +36,7 @@ setup(
install_requires=[
'Django>=1.8',
'Django<1.10',
'django-mailman3',
'django-browserid',
'mailmanclient',
],
......
# Copyright (C) 2011-2012 by the Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
# USA.
"""Cleanse a message for archiving."""
from __future__ import absolute_import, unicode_literals
import os
import re
import binascii
from mimetypes import guess_all_extensions
from email.header import decode_header, make_header
from email.errors import HeaderParseError
pre = re.compile(r'[/\\:]')
sre = re.compile(r'[^-\w.]')
dre = re.compile(r'^\.*')
BR = '<br>\n'
NEXT_PART = re.compile(r'--------------[ ]next[ ]part[ ]--------------\n')
def guess_extension(ctype, ext):
all_exts = guess_all_extensions(ctype, strict=False)
if ext in all_exts:
return ext
return all_exts and all_exts[0]
def get_charset(message, default="ascii", guess=False):
if message.get_content_charset():
return message.get_content_charset().decode("ascii")
if message.get_charset():
return message.get_charset().decode("ascii")
charset = default
if not guess:
return charset
text = message.get_payload(decode=True)
for encoding in ["ascii", "utf-8", "iso-8859-15"]:
try:
text.decode(encoding)
except UnicodeDecodeError:
continue
else:
charset = encoding
break
return charset
def oneline(s):
"""Inspired by mailman.utilities.string.oneline"""
try:
h = make_header(decode_header(s))
ustr = h.__unicode__()
return ''.join(ustr.splitlines())
except (LookupError, UnicodeError, ValueError, HeaderParseError):
return ''.join(s.splitlines())
class Scrubber(object):
def __init__(self, msg):
self.msg = msg
def scrub(self):
attachments = []
for part_num, part in enumerate(self.msg.walk()):
ctype = part.get_content_type()
if not isinstance(ctype, unicode):
ctype = ctype.decode("ascii")
if ctype == 'text/plain':
disposition = part.get('content-disposition')
if disposition and disposition.decode(
"ascii", "replace").strip().startswith("attachment"):
attachments.append(self.parse_attachment(part, part_num))
part.set_payload('')
elif ctype == 'text/html':
attachments.append(self.parse_attachment(part, part_num,
filter_html=False))
part.set_payload('')
elif ctype == 'message/rfc822':
attachments.append(self.parse_attachment(part, part_num))
part.set_payload('')
elif part.get_payload() and not part.is_multipart():
payload = part.get_payload(decode=True)
ctype = part.get_content_type()
if not isinstance(ctype, unicode):
ctype.decode("ascii")
if payload is None:
continue
attachments.append(self.parse_attachment(part, part_num))
if self.msg.is_multipart():
text = []
for part in self.msg.walk():
if not part.get_payload() or part.is_multipart():
continue
partctype = part.get_content_type()
if partctype != 'text/plain' and partctype != 'text/html':
continue
try:
t = part.get_payload(decode=True) or ''
except (binascii.Error, TypeError):
t = part.get_payload() or ''
partcharset = get_charset(part, guess=True)
try:
t = t.decode(partcharset, 'replace')
except (UnicodeError, LookupError, ValueError,
AssertionError):
t = t.decode('ascii', 'replace')
if isinstance(t, basestring):
if not t.endswith('\n'):
t += '\n'
text.append(t)
text = u"\n".join(text)
else:
text = self.msg.get_payload(decode=True)
charset = get_charset(self.msg, guess=True)
try:
text = text.decode(charset, "replace")
except (UnicodeError, LookupError, ValueError, AssertionError):
text = text.decode('ascii', 'replace')
next_part_match = NEXT_PART.search(text)
if next_part_match:
text = text[0:next_part_match.start(0)]
return (text, attachments)
def parse_attachment(self, part, counter, filter_html=True):
decodedpayload = part.get_payload(decode=True)
ctype = part.get_content_type()
if not isinstance(ctype, unicode):
ctype = ctype.decode("ascii")
charset = get_charset(part, default=None, guess=False)
try:
filename = oneline(part.get_filename(''))
except (TypeError, UnicodeDecodeError):
filename = u"attachment.bin"
filename, fnext = os.path.splitext(filename)
ext = fnext or guess_extension(ctype, fnext)
if not ext:
if ctype == 'message/rfc822':
ext = '.txt'
else:
ext = '.bin'
ext = sre.sub('', ext)
if not filename:
filebase = u'attachment'
else:
parts = pre.split(filename)
filename = parts[-1]
filename = dre.sub('', filename)
filename = sre.sub('', filename)
filebase = filename
if ctype == 'message/rfc822':
submsg = part.get_payload()
decodedpayload = str(submsg)
return (counter, filebase + ext, ctype, charset, decodedpayload)
# -*- coding: utf-8 -*-
# Copyright (C) 1998-2016 by the Free Software Foundation, Inc.
#
# This file is part of Postorius.
#
# Postorius is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# Postorius is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along with
# Postorius. If not, see <http://www.gnu.org/licenses/>.
from django import template
from django.utils.html import conditional_escape
register = template.Library()
@register.simple_tag(takes_context=True)
def add_to_query_string(context, *args, **kwargs):
"""Adds or replaces parameters in the query string"""
qs = context["request"].GET.copy()
# create a dict from every args couple
new_qs_elements = dict(zip(args[::2], args[1::2]))
new_qs_elements.update(kwargs)
# don't use the .update() method, it appends instead of overwriting.
for key, value in new_qs_elements.iteritems():
qs[key] = value
return conditional_escape(qs.urlencode())
......@@ -87,7 +87,6 @@ class ListMembersOptionsTest(ViewTestCase):
UserPreferences)
def test_moderation_action(self):
member = self.foo_list.get_member('test@example.com')
self.assertIsNone(
self.foo_list.get_member('test@example.com').moderation_action)
self.client.login(username='testsu', password='testpass')
......
......@@ -21,9 +21,10 @@ import logging
from django.conf import settings
from django.contrib import messages
from django.core.urlresolvers import reverse
from django.test import RequestFactory, TestCase
from django.test import TestCase
from mock import MagicMock
from six.moves.urllib_parse import quote
from django_mailman3.tests.utils import get_flash_messages
from postorius.utils import get_client
from mailmanclient.testing.vcr_helpers import get_vcr
......@@ -88,20 +89,6 @@ def create_mock_member(properties=None):
return mock_object
def get_flash_messages(response, empty=True):
if "messages" not in response.cookies:
return []
# A RequestFactory will not run the messages middleware, and thus will
# not delete the messages after retrieval.
dummy_request = RequestFactory().get("/")
dummy_request.COOKIES["messages"] = response.cookies["messages"].value
msgs = list(messages.storage.cookie.CookieStorage(dummy_request))
if empty:
del response.client.cookies["messages"]
return msgs
get_flash_messages.__test__ = False
class ViewTestCase(TestCase):
use_vcr = True
......
......@@ -28,7 +28,7 @@ from django.core.urlresolvers import reverse
from postorius.auth.decorators import list_moderator_required
from postorius.models import List
from postorius.lib.scrub import Scrubber
from django_mailman3.lib.scrub import Scrubber
def parse(message):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment