Merge dkimpy-milter:dkg/python3 into dkimpy-milter:1_0

Proposed by dkg on 2019-02-22
Status: Merged
Merge reported by: Scott Kitterman
Merged at revision: ea09bab1a8fb9eeed3429e9a8411ee42f9c423f3
Proposed branch: dkimpy-milter:dkg/python3
Merge into: dkimpy-milter:1_0
Prerequisite: dkimpy-milter:dkg/test-suite
Diff against target: 327 lines (+55/-56)
6 files modified
dkimpy_milter/__init__.py (+31/-32)
dkimpy_milter/__main__.py (+1/-1)
dkimpy_milter/config.py (+10/-10)
dkimpy_milter/dnsplug.py (+8/-8)
setup.py (+4/-4)
tests/dkimpy-milter (+1/-1)
Reviewer Review Type Date Requested Status
Scott Kitterman 2019-02-22 Pending
Review via email: mp+363528@code.launchpad.net

Commit message

This brief series converts dkimpy-milter to python3. It depends on the test-suite branch to verify that the major functionality remains functional.

To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/dkimpy_milter/__init__.py b/dkimpy_milter/__init__.py
2index 5345fc7..bcdf2a7 100644
3--- a/dkimpy_milter/__init__.py
4+++ b/dkimpy_milter/__init__.py
5@@ -1,4 +1,4 @@
6-#! /usr/bin/python2
7+#! /usr/bin/python3
8 # Original dkim-milter.py code:
9 # Author: Stuart D. Gathman <stuart@bmsi.com>
10 # Copyright 2007 Business Management Systems, Inc.
11@@ -28,8 +28,9 @@ import dkim
12 import authres
13 import os
14 import tempfile
15-import StringIO
16+import io
17 import re
18+import codecs
19 from Milter.utils import parse_addr, parseaddr
20 import dkimpy_milter.config as config
21 from dkimpy_milter.util import drop_privileges
22@@ -110,7 +111,7 @@ class dkimMilter(Milter.Base):
23 def envfrom(self, f, *str):
24 if milterconfig.get('Syslog') and milterconfig.get('debugLevel') >= 2:
25 syslog.syslog("mail from: {0} {1}".format(f, str))
26- self.fp = StringIO.StringIO()
27+ self.fp = io.BytesIO()
28 self.mailfrom = f
29 t = parse_addr(f)
30 if len(t) == 2:
31@@ -142,13 +143,13 @@ class dkimMilter(Milter.Base):
32 elif lname == 'authentication-results':
33 self.arheaders.append(val)
34 if self.fp:
35- self.fp.write("%s: %s\n" % (name, val))
36+ self.fp.write(b"%s: %s\n" % (codecs.encode(name, 'ascii'), codecs.encode(val, 'ascii')))
37 return Milter.CONTINUE
38
39 @Milter.noreply
40 def eoh(self):
41 if self.fp:
42- self.fp.write("\n") # terminate headers
43+ self.fp.write(b"\n") # terminate headers
44 self.bodysize = 0
45 return Milter.CONTINUE
46
47@@ -195,20 +196,20 @@ class dkimMilter(Milter.Base):
48 h = authres.AuthenticationResultsHeader(authserv_id=
49 self.AuthservID,
50 results=self.arresults)
51- h = fold(str(h))
52+ h = fold(codecs.encode(str(h), 'ascii'))
53 if (milterconfig.get('Syslog') and
54 milterconfig.get('debugLevel') >= 2):
55- syslog.syslog(str(h))
56- name, val = str(h).split(': ', 1)
57+ syslog.syslog(codecs.decode(h, 'ascii'))
58+ name, val = codecs.decode(h, 'ascii').split(': ', 1)
59 self.addheader(name, val, 0)
60 return Milter.CONTINUE
61
62 def sign_dkim(self, txt):
63- canon = milterconfig.get('Canonicalization')
64+ canon = codecs.encode(milterconfig.get('Canonicalization'), 'ascii')
65 canonicalize = []
66- if len(canon.split('/')) == 2:
67- canonicalize.append(canon.split('/')[0])
68- canonicalize.append(canon.split('/')[1])
69+ if len(canon.split(b'/')) == 2:
70+ canonicalize.append(canon.split(b'/')[0])
71+ canonicalize.append(canon.split(b'/')[1])
72 else:
73 canonicalize.append(canon)
74 canonicalize.append(canon)
75@@ -218,11 +219,12 @@ class dkimMilter(Milter.Base):
76 try:
77 if privateRSA:
78 d = dkim.DKIM(txt)
79- h = d.sign(milterconfig.get('Selector'), self.fdomain,
80- privateRSA, canonicalize=(canonicalize[0],
81- canonicalize[1]))
82- name, val = h.split(': ', 1)
83- self.addheader(name, val.strip().replace('\r\n', '\n'), 0)
84+ h = d.sign(codecs.encode(milterconfig.get('Selector'), 'ascii'), codecs.encode(self.fdomain, 'ascii'),
85+ codecs.encode(privateRSA, 'ascii'),
86+ canonicalize=(canonicalize[0],
87+ canonicalize[1]))
88+ name, val = h.split(b': ', 1)
89+ self.addheader(codecs.decode(name, 'ascii'), codecs.decode(val, 'ascii').strip().replace('\r\n', '\n'), 0)
90 if (milterconfig.get('Syslog') and
91 (milterconfig.get('SyslogSuccess')
92 or milterconfig.get('debugLevel') >= 1)):
93@@ -233,12 +235,12 @@ class dkimMilter(Milter.Base):
94 d.domain.lower()))
95 if privateEd25519:
96 d = dkim.DKIM(txt)
97- h = d.sign(milterconfig.get('SelectorEd25519'), self.fdomain,
98+ h = d.sign(codecs.encode(milterconfig.get('SelectorEd25519'), 'ascii'), codecs.encode(self.fdomain, 'ascii'),
99 privateEd25519, canonicalize=(canonicalize[0],
100 canonicalize[1]),
101- signature_algorithm='ed25519-sha256')
102- name, val = h.split(': ', 1)
103- self.addheader(name, val.strip().replace('\r\n', '\n'), 0)
104+ signature_algorithm=b'ed25519-sha256')
105+ name, val = h.split(b': ', 1)
106+ self.addheader(codecs.decode(name, 'ascii'), codecs.decode(val, 'ascii').strip().replace('\r\n', '\n'), 0)
107 if (milterconfig.get('Syslog') and
108 (milterconfig.get('SyslogSuccess')
109 or milterconfig.get('debugLevel') >= 1)):
110@@ -266,20 +268,17 @@ class dkimMilter(Milter.Base):
111 res = d.verify(idx=y, dnsfunc=lambda _x: dnsoverride)
112 else:
113 res = d.verify(idx=y)
114+ algo = codecs.decode(d.signature_fields.get(b'a'), 'ascii')
115 if res:
116- if d.signature_fields.get(b'a') == 'ed25519-sha256':
117+ if algo == 'ed25519-sha256':
118 self.dkim_comment = ('Good {0} signature'
119- .format(d.signature_fields
120- .get(b'a')))
121+ .format(algo))
122 else:
123 self.dkim_comment = ('Good {0} bit {1} signature'
124- .format(d.keysize,
125- d.signature_fields
126- .get(b'a')))
127+ .format(d.keysize, algo))
128 else:
129 self.dkim_comment = ('Bad {0} bit {1} signature.'
130- .format(d.keysize,
131- d.signature_fields.get(b'a')))
132+ .format(d.keysize, algo))
133 except dkim.DKIMException as x:
134 self.dkim_comment = str(x)
135 if milterconfig.get('Syslog'):
136@@ -288,9 +287,9 @@ class dkimMilter(Milter.Base):
137 self.dkim_comment = str(x)
138 if milterconfig.get('Syslog'):
139 syslog.syslog("check_dkim: {0}".format(x))
140- self.header_i = d.signature_fields.get(b'i')
141- self.header_d = d.signature_fields.get(b'd')
142- self.header_a = d.signature_fields.get(b'a')
143+ self.header_i = codecs.decode(d.signature_fields.get(b'i'), 'ascii')
144+ self.header_d = codecs.decode(d.signature_fields.get(b'd'), 'ascii')
145+ self.header_a = codecs.decode(d.signature_fields.get(b'a'), 'ascii')
146 if res:
147 if (milterconfig.get('Syslog') and
148 (milterconfig.get('SyslogSuccess') or
149diff --git a/dkimpy_milter/__main__.py b/dkimpy_milter/__main__.py
150index 8c5cf9c..f95075f 100644
151--- a/dkimpy_milter/__main__.py
152+++ b/dkimpy_milter/__main__.py
153@@ -1,4 +1,4 @@
154-#!/usr/bin/python2
155+#!/usr/bin/python3
156
157 from dkimpy_milter import main
158
159diff --git a/dkimpy_milter/config.py b/dkimpy_milter/config.py
160index d562e97..bf6551a 100644
161--- a/dkimpy_milter/config.py
162+++ b/dkimpy_milter/config.py
163@@ -31,13 +31,13 @@ import stat
164 import dkim
165 import socket
166 import ipaddress
167-from dnsplug import Session
168+from .dnsplug import Session
169
170 # default values
171 defaultConfigData = {
172 'Syslog': 'yes',
173 'SyslogFacility': 'mail',
174- 'UMask': 007,
175+ 'UMask': 0o07,
176 'Mode': 'sv',
177 'Socket': 'local:/var/run/dkimpy-milter/dkimpy-milter.sock',
178 'PidFile': '/var/run/dkimpy-milter/dkimpy-milter.pid',
179@@ -85,14 +85,14 @@ class HostsDataset(object):
180 self.item = item[1:]
181 self.negative = True
182 try:
183- self.item = ipaddress.ip_address(unicode(self.item, "utf-8"))
184+ self.item = ipaddress.ip_address(str(self.item, "utf-8"))
185 if isinstance(self.item, ipaddress.IPv4Address):
186 self.isipv4 = True
187 elif isinstance(self.item, ipaddress.IPv6Address):
188 self.isipv6 = True
189 except ValueError as e:
190 try:
191- self.item = ipaddress.ip_network(unicode
192+ self.item = ipaddress.ip_network(str
193 (self.item, "utf-8"),
194 strict=False)
195 if isinstance(self.item, ipaddress.IPv4Network):
196@@ -110,7 +110,7 @@ class HostsDataset(object):
197
198 def match(self, connectip):
199 '''Check if the connect IP is part of the dataset'''
200- source = ipaddress.ip_address(unicode(connectip, "utf-8"))
201+ source = ipaddress.ip_address(str(connectip, "utf-8"))
202 for item in self.dataset:
203 if item.isdomain or item.ishostname:
204 result = self.matchname(source) # Match host/domains first
205@@ -160,13 +160,13 @@ class HostsDataset(object):
206 if isinstance(source, ipaddress.IPv4Address):
207 ips = s.dns(name, 'A')
208 for ip in ips:
209- ip = ipaddress.IPv4Address(unicode(ip, 'UTF-8'))
210+ ip = ipaddress.IPv4Address(str(ip, 'UTF-8'))
211 if ip == source:
212 results.append(name)
213 if isinstance(source, ipaddress.IPv6Address):
214 ips = s.dns(name, 'AAAA')
215 for ip in ips:
216- ip = ipaddress.IPv6Address(unicode(ip, 'UTF-8'))
217+ ip = ipaddress.IPv6Address(str(ip, 'UTF-8'))
218 if ip == source:
219 results.append(name)
220 return results
221@@ -225,13 +225,13 @@ def _processConfigFile(filename=None, configdata=None, useSyslog=1,
222 '''Load the specified config file, exit and log errors if it fails,
223 otherwise return a config dictionary.'''
224
225- import config
226+ from . import config
227 if configdata is None:
228 configdata = config.defaultConfigData
229 if filename is not None:
230 try:
231 _readConfigFile(filename, configdata)
232- except Exception, e:
233+ except Exception as e:
234 raise
235 if useSyslog:
236 syslog.syslog(e.args[0])
237@@ -342,7 +342,7 @@ def _readConfigFile(path, configData=None, configGlobal={}):
238 # check to see if it's a file
239 try:
240 mode = os.stat(path)[0]
241- except OSError, e:
242+ except OSError as e:
243 syslog.syslog(syslog.LOG_ERR, 'ERROR stating "%s": %s'
244 % (path, e.strerror))
245 return(configData)
246diff --git a/dkimpy_milter/dnsplug.py b/dkimpy_milter/dnsplug.py
247index aa357ec..33067e9 100644
248--- a/dkimpy_milter/dnsplug.py
249+++ b/dkimpy_milter/dnsplug.py
250@@ -84,7 +84,7 @@ class Session(object):
251 raise DNSError('Length of CNAME chain exceeds %d' % MAX_CNAME)
252 cnames[name] = cname
253 if cname in cnames:
254- raise DNSError, 'CNAME loop'
255+ raise DNSError('CNAME loop')
256 result = self.dns(cname, qtype, cnames=cnames)
257 return result
258
259@@ -103,16 +103,16 @@ def DNSLookup_pydns(name, qtype, tcpfallback=True, timeout=30):
260 #
261 if resp.header['tc'] == True:
262 if not tcpfallback:
263- raise DNS.DNSError, 'DNS: Truncated UDP Reply, SPF records should fit in a UDP packet'
264+ raise DNS.DNSError('DNS: Truncated UDP Reply, SPF records should fit in a UDP packet')
265 try:
266 req = DNS.DnsRequest(name, qtype=qtype, protocol='tcp',
267 timeout=timeout)
268 resp = req.req()
269- except DNS.DNSError, x:
270- raise DNS.DNSError, 'TCP Fallback error: ' + str(x)
271+ except DNS.DNSError as x:
272+ raise DNS.DNSError('TCP Fallback error: ' + str(x))
273 return [((a['name'], a['typename']), a['data']) for a in resp.answers]
274- except IOError, x:
275- raise DNS.DNSError, 'DNS: ' + str(x)
276+ except IOError as x:
277+ raise DNS.DNSError('DNS: ' + str(x))
278
279 def DNSLookup_dnspython(name,qtype,tcpfallback=True,timeout=30):
280 retVal = []
281@@ -164,5 +164,5 @@ if __name__ == '__main__':
282 import sys
283 s = Session()
284 for n,t in zip(*[iter(sys.argv[1:])]*2):
285- print n,t
286- print s.dns(n,t)
287+ print(n,t)
288+ print(s.dns(n,t))
289diff --git a/setup.py b/setup.py
290index 18c2ec9..c7990d7 100644
291--- a/setup.py
292+++ b/setup.py
293@@ -1,4 +1,4 @@
294-#! /usr/bin/python
295+#! /usr/bin/python3
296 # dkimpy-milter: A DKIM signing/verification Milter application
297 # Author: Scott Kitterman <scott@kitterman.com>
298 # Copyright 2018 Scott Kitterman
299@@ -24,9 +24,9 @@ description = "Domain Keys Identified Mail (DKIM) signing/verifying milter for P
300 kw = {} # Work-around for lack of 'or' requires in setuptools.
301 try:
302 import dns
303- kw['install_requires'] = ['dkimpy>=0.7', 'pymilter', 'authres>=1.1.0', 'PyNaCl', 'ipaddress', 'dnspython']
304+ kw['install_requires'] = ['dkimpy>=0.7', 'pymilter', 'authres>=1.1.0', 'PyNaCl', 'dnspython']
305 except ImportError: # If PyDNS is not installed, prefer dnspython
306- kw['install_requires'] = ['dkimpy>=0.7', 'pymilter', 'authres>=1.1.0', 'PyNaCl', 'ipaddress', 'PyDNS']
307+ kw['install_requires'] = ['dkimpy>=0.7', 'pymilter', 'authres>=1.1.0', 'PyNaCl', 'PyDNS']
308
309 setup(
310 name='dkimpy-milter',
311@@ -43,7 +43,7 @@ setup(
312 'License :: OSI Approved :: GNU General Public License (GPL)',
313 'Natural Language :: English',
314 'Operating System :: POSIX',
315- 'Programming Language :: Python :: 2 :: Only',
316+ 'Programming Language :: Python :: 3 :: Only',
317 'Topic :: Communications :: Email :: Mail Transport Agents',
318 'Topic :: Communications :: Email :: Filters',
319 'Topic :: Security',
320diff --git a/tests/dkimpy-milter b/tests/dkimpy-milter
321index 39b64d5..9ee02e9 100755
322--- a/tests/dkimpy-milter
323+++ b/tests/dkimpy-milter
324@@ -1,2 +1,2 @@
325 #!/bin/sh
326-python2 -m dkimpy_milter "$@"
327+python3 -m dkimpy_milter "$@"

Subscribers

People subscribed via source and target branches

to all changes: