Ankush Sharma has proposed merging
lp:~black-perl/mailman.client/handling-special-chars-in-email into
lp:mailman.client.
Requested reviews:
Mailman Coders (mailman-coders)
Related bugs:
Bug #1429366 in GNU Mailman REST Client: "Anatomy of list ids does not keep
with that of urls causes some REST end points to return 404 always"
https://bugs.launchpad.net/mailman.client/+bug/1429366
For more details, see:
https://code.launchpad.net/~black-perl/mailman.client/handling-special-chars-in-email/+merge/252899
As discussed in the bug report, use of emails with special character set is
valid as per email RFCs but postorius crashes on using them giving 404 or
KeyError always.
As discussed on the mailing list a possible solution would be to percent encode
these special characters when they appear in the list_id or fqdn_listname
before sending a request to the REST server and decoding on the other end.
Added utils.py which has to functions `encode` and `encode_url` to faciliate
encoding of list_id's in URLs when required.
The use of special characters in list_ids is working properly
http://oi58.tinypic.com/33u689i.jpg.
--
Your team Mailman Coders is requested to review the proposed merge of
lp:~black-perl/mailman.client/handling-special-chars-in-email into
lp:mailman.client.
=== modified file 'src/mailmanclient/_client.py'
--- src/mailmanclient/_client.py 2015-01-04 22:43:40 +0000
+++ src/mailmanclient/_client.py 2015-03-13 14:16:16 +0000
@@ -33,7 +33,9 @@
from mailmanclient import __version__
from operator import itemgetter
from six.moves.urllib_error import HTTPError
-from six.moves.urllib_parse import urlencode, urljoin
+from six.moves.urllib_parse import urlencode, urljoin, quote
+
+from .utils import encode_url
DEFAULT_PAGE_ITEM_COUNT = 50
@@ -247,13 +249,15 @@
return _Address(self._connection, content)
def get_list(self, fqdn_listname):
+ encd_fqdn_listname = quote(fqdn_listname)
response, content = self._connection.call(
- 'lists/{0}'.format(fqdn_listname))
+ 'lists/{0}'.format(encd_fqdn_listname))
return _List(self._connection, content['self_link'], content)
def delete_list(self, fqdn_listname):
+ encd_fqdn_listname = quote(fqdn_listname)
response, content = self._connection.call(
- 'lists/{0}'.format(fqdn_listname), None, 'DELETE')
+ 'lists/{0}'.format(encd_fqdn_listname), None, 'DELETE')
class _Domain:
@@ -319,7 +323,7 @@
def __init__(self, connection, url, data=None):
self._connection = connection
- self._url = url
+ self._url = encode_url(url)
self._info = data
def __repr__(self):
@@ -331,6 +335,14 @@
self._info = content
@property
+ def enc_fqdn_listname(self):
+ """
+ Returns fqdn_listname in encoded form
+ """
+ self._get_info()
+ return quote(self._info['fqdn_listname'])
+
+ @property
def owners(self):
url = self._url + '/roster/owner'
response, content = self._connection.call(url)
@@ -375,7 +387,7 @@
@property
def members(self):
- url = 'lists/{0}/roster/member'.format(self.fqdn_listname)
+ url = 'lists/{0}/roster/member'.format(self.enc_fqdn_listname)
response, content = self._connection.call(url)
if 'entries' not in content:
return []
@@ -398,19 +410,19 @@
key=itemgetter('address'))]
def get_member_page(self, count=50, page=1):
- url = 'lists/{0}/roster/member'.format(self.fqdn_listname)
+ url = 'lists/{0}/roster/member'.format(self.enc_fqdn_listname)
return _Page(self._connection, url, _Member, count, page)
@property
def settings(self):
return _Settings(self._connection,
- 'lists/{0}/config'.format(self.fqdn_listname))
+ 'lists/{0}/config'.format(self.enc_fqdn_listname))
@property
def held(self):
"""Return a list of dicts with held message information."""
response, content = self._connection.call(
- 'lists/{0}/held'.format(self.fqdn_listname), None, 'GET')
+ 'lists/{0}/held'.format(self.enc_fqdn_listname), None, 'GET')
if 'entries' not in content:
return []
else:
@@ -429,7 +441,7 @@
def requests(self):
"""Return a list of dicts with subscription requests."""
response, content = self._connection.call(
- 'lists/{0}/requests'.format(self.fqdn_listname), None, 'GET')
+ 'lists/{0}/requests'.format(self.enc_fqdn_listname), None, 'GET')
if 'entries' not in content:
return []
else:
@@ -465,7 +477,7 @@
self.remove_role('moderator', address)
def remove_role(self, role, address):
- url = 'lists/%s/%s/%s' % (self.fqdn_listname, role, address)
+ url = 'lists/%s/%s/%s' % (self.enc_fqdn_listname, role, address)
self._connection.call(url, method='DELETE')
def moderate_message(self, request_id, action):
@@ -477,7 +489,7 @@
:type action: String.
"""
path = 'lists/{0}/held/{1}'.format(
- self.fqdn_listname, str(request_id))
+ self.enc_fqdn_listname, str(request_id))
response, content = self._connection.call(
path, dict(action=action), 'POST')
return response
@@ -548,7 +560,7 @@
def delete(self):
response, content = self._connection.call(
- 'lists/{0}'.format(self.fqdn_listname), None, 'DELETE')
+ 'lists/{0}'.format(self.enc_fqdn_listname), None, 'DELETE')
class _Member:
@@ -1002,3 +1014,4 @@
def files(self):
response, content = self._connection.call(self.url)
return content['files']
+
=== added file 'src/mailmanclient/utils.py'
--- src/mailmanclient/utils.py 1970-01-01 00:00:00 +0000
+++ src/mailmanclient/utils.py 2015-03-13 14:16:16 +0000
@@ -0,0 +1,22 @@
+"""
+ Utility function for percent encoding and decoding url parts
+"""
+
+from six.moves.urllib_parse import quote
+
+def encode_url(url):
+ """
+ Takes a url as parameter and percent encode the list_id only and
+ returns the percent encoded url
+ :param url: URL to be encoded
+ :return: Percent encoded form of `url`
+ """
+ # url segments after splitting
+ segments = url.split('/')
+ # segment to keep same
+ segment1 = '/'.join(segments[:5])
+ # segment to encode
+ segment2 = '/'.join(segments[5:])
+ segment2 = quote(segment2)
+ # make the url back from the segments
+ return ( segment1 + '/' + segment2)
\ No newline at end of file
_______________________________________________
Mailman-coders mailing list
[email protected]
https://mail.python.org/mailman/listinfo/mailman-coders