From 36086deed1873090bee2de7a7a86ddb4e7deb1d2 Mon Sep 17 00:00:00 2001 From: Sandino Araico Sanchez Date: Tue, 9 Jun 2020 02:41:09 -0500 Subject: [PATCH] patches for ebuild from versions in fedora package --- mail-filter/pyzor/files/65.patch | 42 + ...d76d6b86ea2e9076a9c34ffd4651909d214b7.diff | 2129 +++++++++++++++++ mail-filter/pyzor/pyzor-9999.ebuild | 69 + 3 files changed, 2240 insertions(+) create mode 100644 mail-filter/pyzor/files/65.patch create mode 100644 mail-filter/pyzor/files/commit-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7.diff create mode 100644 mail-filter/pyzor/pyzor-9999.ebuild diff --git a/mail-filter/pyzor/files/65.patch b/mail-filter/pyzor/files/65.patch new file mode 100644 index 0000000..575e93d --- /dev/null +++ b/mail-filter/pyzor/files/65.patch @@ -0,0 +1,42 @@ +From 6332a429ed415187599ecce7d8a169ee19f0bbe5 Mon Sep 17 00:00:00 2001 +From: Michael Orlitzky +Date: Sun, 4 Mar 2018 17:34:33 -0500 +Subject: [PATCH] scripts/pyzor: read stdin as binary in _get_input_msg(). + +Reading stdin in python-3.x is done as text, with a best-guess +encoding. But this can go awry: for example, if an iso-8859-1 message +is passed in and if python guesses the "utf-8" encoding, then read() +will fail with a UnicodeDecodeError on non-ASCII characters. For +example, the "copyright" symbol is a single byte 0xa9 in iso-8859-1, +and the utf-8 decoder can't handle it: + + UnicodeDecodeError: 'utf-8' codec can't decode byte 0xa9... invalid + start byte + +Instead -- and as was done in python-2.x -- we can read stdin as +binary using the new get_binary_stdin() function. Afterwards, we use +email.message_from_bytes() instead of the email.message_from_file() +constructor to parse the byte data. The resulting function is able to +correctly parse these messages. + +Closes: https://github.com/SpamExperts/pyzor/issues/64 +--- + scripts/pyzor | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/scripts/pyzor b/scripts/pyzor +index 567a7f9..1ba632f 100755 +--- a/scripts/pyzor ++++ b/scripts/pyzor +@@ -171,7 +171,10 @@ def _get_input_digests(dummy): + + + def _get_input_msg(digester): +- msg = email.message_from_file(sys.stdin) ++ # Read and process stdin as bytes because we don't know its ++ # encoding. Python-3.x will try to guess -- and can sometimes ++ # guess wrong -- leading to decoding errors in read(). ++ msg = email.message_from_bytes(get_binary_stdin().read()) + digested = digester(msg).value + yield digested + diff --git a/mail-filter/pyzor/files/commit-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7.diff b/mail-filter/pyzor/files/commit-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7.diff new file mode 100644 index 0000000..b3b33a4 --- /dev/null +++ b/mail-filter/pyzor/files/commit-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7.diff @@ -0,0 +1,2129 @@ +diff -puriN pyzor-release-1-0-0/.codeclimate.yml pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/.codeclimate.yml +--- pyzor-release-1-0-0/.codeclimate.yml 1969-12-31 18:00:00.000000000 -0600 ++++ pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/.codeclimate.yml 2018-05-09 04:35:23.000000000 -0500 +@@ -0,0 +1,30 @@ ++engines: ++ duplication: ++ enabled: true ++ config: ++ languages: ++ - python ++ fixme: ++ enabled: true ++ radon: ++ enabled: true ++ pep8: ++ enabled: true ++ shellcheck: ++ enabled: true ++ratings: ++ paths: ++ - "**.py" ++ - "scripts/*" ++exclude_paths: ++- tests/ ++- docs/ ++- config/ ++- web/ ++- README.rst ++- COPYING ++- INSTALL ++- MANIFEST.in ++- THANKS ++- requirements.txt ++- test.conf +diff -puriN pyzor-release-1-0-0/.github/issue_template.md pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/.github/issue_template.md +--- pyzor-release-1-0-0/.github/issue_template.md 1969-12-31 18:00:00.000000000 -0600 ++++ pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/.github/issue_template.md 2018-05-09 04:35:23.000000000 -0500 +@@ -0,0 +1,14 @@ ++### Version information ++ ++ ++### Steps to replicate ++ ++ 1. ++ ++### Actual result ++ ++ ++### Expected result ++ ++ ++### Other notes +diff -puriN pyzor-release-1-0-0/.travis.yml pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/.travis.yml +--- pyzor-release-1-0-0/.travis.yml 2014-12-10 07:48:21.000000000 -0600 ++++ pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/.travis.yml 2018-05-09 04:35:23.000000000 -0500 +@@ -13,3 +13,7 @@ script: + - "py.test tests/functional/" + after_success: + - coveralls ++notifications: ++ slack: ++ secure: cjCoL6zEvbUGejQ39Xu4MNZHipxPux0+DG450js8S5L4+hSsch0FlHY1CP/Eeie0ko9QELhdWG1dB7Ndb7HQ5WVJsOhOA9mFYM5rBam4LZS1rv0Ggf0ASnfk0HDHiL0+f03ssneDNP4D8FhCdZl4FPgBPIUv2l+mTcWxHeoY4zM= ++ +diff -puriN pyzor-release-1-0-0/COPYING pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/COPYING +--- pyzor-release-1-0-0/COPYING 2014-12-10 07:48:21.000000000 -0600 ++++ pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/COPYING 2018-05-09 04:35:23.000000000 -0500 +@@ -1,12 +1,12 @@ +- GNU GENERAL PUBLIC LICENSE +- Version 2, June 1991 ++ GNU GENERAL PUBLIC LICENSE ++ Version 2, June 1991 + +- Copyright (C) 1989, 1991 Free Software Foundation, Inc. +- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ Copyright (C) 1989, 1991 Free Software Foundation, Inc., ++ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +- Preamble ++ Preamble + + The licenses for most software are designed to take away your + freedom to share and change it. By contrast, the GNU General Public +@@ -15,7 +15,7 @@ software--to make sure the software is f + General Public License applies to most of the Free Software + Foundation's software and to any other program whose authors commit to + using it. (Some other Free Software Foundation software is covered by +-the GNU Library General Public License instead.) You can apply it to ++the GNU Lesser General Public License instead.) You can apply it to + your programs, too. + + When we speak of free software, we are referring to freedom, not +@@ -55,8 +55,8 @@ patent must be licensed for everyone's f + + The precise terms and conditions for copying, distribution and + modification follow. +- +- GNU GENERAL PUBLIC LICENSE ++ ++ GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +@@ -110,7 +110,7 @@ above, provided that you also meet all o + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) +- ++ + These requirements apply to the modified work as a whole. If + identifiable sections of that work are not derived from the Program, + and can be reasonably considered independent and separate works in +@@ -168,7 +168,7 @@ access to copy from a designated place, + access to copy the source code from the same place counts as + distribution of the source code, even though third parties are not + compelled to copy the source along with the object code. +- ++ + 4. You may not copy, modify, sublicense, or distribute the Program + except as expressly provided under this License. Any attempt + otherwise to copy, modify, sublicense or distribute the Program is +@@ -225,7 +225,7 @@ impose that choice. + + This section is intended to make thoroughly clear what is believed to + be a consequence of the rest of this License. +- ++ + 8. If the distribution and/or use of the Program is restricted in + certain countries either by patents or by copyrighted interfaces, the + original copyright holder who places the Program under this License +@@ -255,7 +255,7 @@ make exceptions for this. Our decision + of preserving the free status of all derivatives of our free software and + of promoting the sharing and reuse of software generally. + +- NO WARRANTY ++ NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY + FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +@@ -277,9 +277,9 @@ YOU OR THIRD PARTIES OR A FAILURE OF THE + PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGES. + +- END OF TERMS AND CONDITIONS +- +- How to Apply These Terms to Your New Programs ++ END OF TERMS AND CONDITIONS ++ ++ How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest + possible use to the public, the best way to achieve this is to make it +@@ -291,7 +291,7 @@ convey the exclusion of warranty; and ea + the "copyright" line and a pointer to where the full notice is found. + + +- Copyright (C) 19yy ++ Copyright (C) + + 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 +@@ -303,17 +303,16 @@ the "copyright" line and a pointer to wh + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +- ++ 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. + + Also add information on how to contact you by electronic and paper mail. + + If the program is interactive, make it output a short notice like this + when it starts in an interactive mode: + +- Gnomovision version 69, Copyright (C) 19yy name of author ++ Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. +@@ -336,5 +335,5 @@ necessary. Here is a sample; alter the + This General Public License does not permit incorporating your program into + proprietary programs. If your program is a subroutine library, you may + consider it more useful to permit linking proprietary applications with the +-library. If this is what you want to do, use the GNU Library General ++library. If this is what you want to do, use the GNU Lesser General + Public License instead of this License. +diff -puriN pyzor-release-1-0-0/README.rst pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/README.rst +--- pyzor-release-1-0-0/README.rst 2014-12-10 07:48:21.000000000 -0600 ++++ pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/README.rst 2018-05-09 04:35:23.000000000 -0500 +@@ -17,8 +17,12 @@ Pyzor is a Python implementation of a sp + :target: https://travis-ci.org/SpamExperts/pyzor + :alt: Build status + +-.. image:: https://coveralls.io/repos/SpamExperts/pyzor/badge.png?branch=master +- :target: https://coveralls.io/r/SpamExperts/pyzor?branch=master ++.. image:: https://coveralls.io/repos/SpamExperts/pyzor/badge.svg?branch=master&service=github ++ :target: https://coveralls.io/github/SpamExperts/pyzor?branch=master ++ ++.. image:: https://requires.io/github/SpamExperts/pyzor/requirements.svg?branch=requires-io-master ++ :target: https://requires.io/github/SpamExperts/pyzor/requirements/?branch=requires-io-master ++ :alt: Requirements Status + + + +diff -puriN pyzor-release-1-0-0/docs/.static/tony.key pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/docs/.static/tony.key +--- pyzor-release-1-0-0/docs/.static/tony.key 1969-12-31 18:00:00.000000000 -0600 ++++ pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/docs/.static/tony.key 2018-05-09 04:35:23.000000000 -0500 +@@ -0,0 +1,52 @@ ++-----BEGIN PGP PUBLIC KEY BLOCK----- ++Version: GnuPG v1 ++ ++mQINBFp9fPoBEADOYGNha6IvoU0XIo4ZmQs3ZqcfCcScl1maDXUYFHbvbKr7Ow7d ++tid+jiebq9Helmco3jB51k/xUMguYVtiQK0uVZp3jUYztJS35jX5XUln9OI85L5t ++RFzUegzjsj3/ZGWGnSM7qRNuPWIetFZCfukfXFWOWDMeT36tmQzKLeZSHcuRlH7m ++cL3Y9ye+FiADECU6hjulMnnRwAJJLQRTN2ieJovGM3l2UKlqAnlk01EHhZ4mqcHJ ++iNvL1DSFeHhx9T8yU/QIDqAgY632ditPZ0iPlLLCYB6gejWt1wJrheQJGeKfGR0w ++Dj1ZdfP3n+OyLgSJ1xG5FIAo8cp8G6IbBBJXj1/oUQ51Otxt0md5hmSfj4gqwPiC ++9mINWyJAnzUZlKHG2SZHV8vAaYxPB/iF3tkWcpQ0BODNeoSMPnOV/repTi46+n/e ++sG++rktWUfpz2DKEy2hTg3GkD3d9MxuZMMkZrzOJ0HjJiaNpp8GCZevt+uGeQPaa ++X1gDaF4rLFwvKVreGEzH7YUifvgQ8RyxPQCi+4wDx7Zt+vuhSQuzJMIKJPkfVAWL ++EP5drB1FUVfYUuwB6dtzMkFvhlNxFGDOjHGV5CDk6JiNXTYct33cWr1byOW7qK2U ++uPZTM5rIMYPjQDTkETfpRQkfK4ecM9kGsJ2vYsHZ0Q6Ky09Xrz7a5wzAHwARAQAB ++tDtUb255IE1leWVyIChQbGF0Zm9ybSBBcmNoaXRlY3QpIDx0b255Lm1leWVyQHNv ++bGFyd2luZHMuY29tPokCOAQTAQIAIgUCWn18+gIbAwYLCQgHAwIGFQgCCQoLBBYC ++AwECHgECF4AACgkQ+bkyK4Jv2TVs/w/8C9SkDZGGGhN0NOvMSBAiT5z2aX7mn1cb ++8cUXk45XPf4nKrAgOV3oD+mdGBzkphlXcW3ubxo4qS/GkPMlSGgD8UBtMZ/1FtNH ++H/APmF4HQEWPGQJyVPxF6ExkxnVu1AYZDejwdZHtDbvxmFAzpfFVa8T8AwH4VSHz ++ySoXLawR4rpibFcmDgYQtoChhGnB+cuBD3bjv8K5oGM5s3qUQTmkMTTMOSWdzBb1 ++J7eTQ0uMITbcLnRtraqTgflHlDT80floOep0QmCQAbm8JH62riHa2rOkPr4Ojz1P ++bj7L1frG1zV+TlOxeXn2v0RNRYbuFGPLBqNo19Bf4SLxHS4TSnIM7fZZM/aQHZMg ++hn5M5/kF29fmma3XX5j9fwru5Fq0yXYOFvGfd2D1tE8+xF33iUvpiNfk6ALy4c2X ++Icy4FIj0zaZDjl7eieZQExi1OudaZkXqi0UdNq1n7XVAgnQAGeHlfioVctHAnETL ++9cNcI6jTotxvwim6FAr+UrD2WhGzAhe5lOjrpHcWkIyiDh86hUBMa8MonN3DtaYo ++ohZsj7v78zDgByiy+l9HYhDlONJfUWT9o3ucK+kBav6L3Cp/ZuBevPnXO/fK6V/d ++9DGy2gVyXanQEo5vAJuDV6qekR/ACTCaunzJaK1h0jcurXiH6K7GICk9iMiaxpbd ++o18ZEIqsRp25Ag0EWn18+gEQAMzxBRUFPJhgYP6QA5aF4Efr4ASwKhqCl7UxR+vW ++BQ1Y/wsxdO3OII7K886xBPfWm9YwQTh6TbVMI+X/Io3t5Bk4tTfWJ4+9O8hSqp4N ++RhVOhyYTLG+aMy3bU6b63acrNVhclZ8eHbiMFcXIadQpBLsiglXjDT4LFQkHGrI9 ++KO6kt+pZ8ZY9UCJp3Cy/qAMCIawB6cd8PSPPplU678J4Ql7U2lRJe3wP4m3r6Mho ++zV5dFgyLiU2ihO7i5tdILgmmxnCMsDbkYT6YP5VUg0fIRUV4PNa9QrFmJ/oqow3x ++6A5Ql47LCYeTO+BVyHARd7Ne8gkKAzy9b8qdmmDILkUXwzNEY6QewDX/BSFRRMSD ++WKHAWVzOiewE6mDWh66DpMY8/WuGcFvyg1e0mkr9nbbQkWgVM78Emgxymaj1dzmr ++oEUZ7ChmiEG9OX+i5XzOL6UTDBY3mgGsuKSkHlAFlQeWYk9ER0P3MGrXtyJon1mk ++zTeie7FPWIolP/uM34hRTZf7/kcX76HQnU4/wWVpWK/1lwVtwlWEuptpYSv3z6t7 ++LwXZAhfSqyJduQDm2Gk/gC9iSaWOxtpCsglqBrBp2e+85ZGMefPa8wWQg7RFhQpu ++jmwQXo+X0aEilceQd2ir/l0wE5Fd+z3NXtpMxBYWPQXHixA71SxLl/sn7cPYlmnV ++jSCTABEBAAGJAh8EGAECAAkFAlp9fPoCGwwACgkQ+bkyK4Jv2TVH8BAAot95BKl5 ++NCS2KfdpOw/COo8Tj3K0mUYEvde99e5QVLzhbO3OdQiZzra5BWuGrt5to+/o9IPo ++T4Y+RHiLbVl93mj58SYVwjFmzf9bnQuAgIBqPLejreOf1yeY8+NdweIIBYbaYmkD ++kBe73gOrHCOz+55f7ONdkQOa6GAkTP++FWr8+xQM8zjNFS5Y1LTo0jAo82njyAMC ++gnTRNu2gUWbRqAOpoIGIdzU9i3CqGl0KJ4PwaBv91Ffo/wFuLBl//OU9dPnnfpdL ++HSnvL2vuO8U+lA7hV82ycEEphrdbroTug3VicUn1ddR/sr9KpYpLs/W0a2v45Pjj ++krcqxHF+YI/FOW/kZhfUgkMU1MMU3+mhjWMbcMbDjmb+ZPIxaJawbnoWqO60778d ++ecQuSF7i0daL+6U0LKPyEeJCc9nOyea4zo+8bx4/aNOEnMGgpYfuEB9TcXrmG8Fi ++kfG5U8triyDAnqOjR5zHohrwU26lIV8ftDbxUeGnsVToXpTyvcjbRmmpV09jrNOe ++jquU9Ua/Tc6UsTpope8901RNLS69sinb7zlBQrukpMmdh9HCIits83JzdgKnLjHd ++GZ4+I84L/40fEK6xOxA3TpWzL91o8ryUgs9qv8hinVn/DMPzBF2iR7hN6HByAzAC ++0SfjbsjHYNEq1QoMOQWozZi3Jv8d79f9xeo= ++=ArFC ++-----END PGP PUBLIC KEY BLOCK----- +diff -puriN pyzor-release-1-0-0/docs/introduction.rst pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/docs/introduction.rst +--- pyzor-release-1-0-0/docs/introduction.rst 2014-12-10 07:48:21.000000000 -0600 ++++ pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/docs/introduction.rst 2018-05-09 04:35:23.000000000 -0500 +@@ -69,5 +69,6 @@ version you are currently using:: + License + ------- + +-The project is licensed under the +-`GNU GPLv2 `_ license. ++This program is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License `version 2 `_ ++only of the License. +diff -puriN pyzor-release-1-0-0/docs/procmail.rst pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/docs/procmail.rst +--- pyzor-release-1-0-0/docs/procmail.rst 2014-12-10 07:48:21.000000000 -0600 ++++ pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/docs/procmail.rst 2018-05-09 04:35:23.000000000 -0500 +@@ -3,15 +3,17 @@ Procmail + + To use Pyzor in a procmail system, consider using the following simple recipe:: + +- :0 Wc +- | pyzor check :0 a ++ :0 Wc ++ | pyzor check ++ :0 a + pyzor-caught + + If you prefer, you can merely add a header to message marked with Pyzor, + instead of immediately filtering them into a separate folder:: + +- :0 Wc +- | pyzor check :0 Waf ++ :0 Wc ++ | pyzor check ++ :0 Waf + | formail -A 'X-Pyzor: spam' + + +diff -puriN pyzor-release-1-0-0/pyzor/__init__.py pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/pyzor/__init__.py +--- pyzor-release-1-0-0/pyzor/__init__.py 2014-12-10 07:48:21.000000000 -0600 ++++ pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/pyzor/__init__.py 2018-05-09 04:35:23.000000000 -0500 +@@ -2,7 +2,7 @@ + + __author__ = "Frank J. Tobin, ftobin@neverending.org" + __credits__ = "Tony Meyer, Dreas von Donselaar, all the Pyzor contributors." +-__version__ = "1.0.0" ++__version__ = "1.1.0" + + import hashlib + +diff -puriN pyzor-release-1-0-0/pyzor/client.py pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/pyzor/client.py +--- pyzor-release-1-0-0/pyzor/client.py 2014-12-10 07:48:21.000000000 -0600 ++++ pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/pyzor/client.py 2018-05-09 04:35:23.000000000 -0500 +@@ -19,7 +19,7 @@ To create a client, using the anonymous + + To get a digest (of an email.message.Message object, or similar): + +->>> digest = pyzor.digest.get_digest(msg) ++>>> digest = pyzor.digest.DataDigester(msg).value + + To query a server (where address is a (host, port) pair): + +@@ -78,7 +78,7 @@ class Client(object): + if accounts is None: + accounts = {} + self.accounts = dict(((host, int(port)), account) +- for (host, port), account in accounts.iteritems()) ++ for (host, port), account in accounts.items()) + if spec is None: + spec = pyzor.digest.digest_spec + self.spec = spec +@@ -117,9 +117,9 @@ class Client(object): + return self.read_response(sock, msg.get_thread()) + + def _mock_check(self, digests, address=None): +- msg = (b"Code: %s\nDiag: OK\nPV: %s\nThread: 1024\nCount: 0\n" +- b"WL-Count: 0" % (pyzor.message.Response.ok_code, +- pyzor.proto_version)) ++ msg = (u"Code: %s\nDiag: OK\nPV: %s\nThread: 1024\nCount: 0\n" ++ u"WL-Count: 0" % (pyzor.message.Response.ok_code, ++ pyzor.proto_version)).encode('ascii') + return email.message_from_bytes(msg, _class=pyzor.message.Response) + + def send(self, msg, address=("public.pyzor.org", 24441)): +@@ -227,12 +227,12 @@ class BatchClient(Client): + + def force(self): + """Force send any remaining reports.""" +- for address, msg in self.r_requests.iteritems(): ++ for address, msg in self.r_requests.items(): + try: + self.send(msg, address) + except: + continue +- for address, msg in self.w_requests.iteritems(): ++ for address, msg in self.w_requests.items(): + try: + self.send(msg, address) + except: +diff -puriN pyzor-release-1-0-0/pyzor/digest.py pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/pyzor/digest.py +--- pyzor-release-1-0-0/pyzor/digest.py 2014-12-10 07:48:21.000000000 -0600 ++++ pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/pyzor/digest.py 2018-05-09 04:35:23.000000000 -0500 +@@ -105,7 +105,7 @@ class DataDigester(object): + def handle_pieced(self, lines, spec): + """Digest stuff according to the spec.""" + for offset, length in spec: +- for i in xrange(length): ++ for i in range(length): + try: + line = lines[int(offset * len(lines) // 100) + i] + except IndexError: +@@ -118,6 +118,7 @@ class DataDigester(object): + + @classmethod + def normalize(cls, s): ++ s = s.replace("\x00", "") + repl = cls.unwanted_txt_repl + s = cls.longstr_ptrn.sub(repl, s) + s = cls.email_ptrn.sub(repl, s) +@@ -147,8 +148,9 @@ class DataDigester(object): + for part in msg.walk(): + if part.get_content_maintype() == "text": + payload = part.get_payload(decode=True) +- + charset = part.get_content_charset() ++ if charset: ++ charset = charset.replace("\x00", "") + errors = "ignore" + if not charset: + charset = "ascii" +diff -puriN pyzor-release-1-0-0/pyzor/engines/common.py pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/pyzor/engines/common.py +--- pyzor-release-1-0-0/pyzor/engines/common.py 2014-12-10 07:48:21.000000000 -0600 ++++ pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/pyzor/engines/common.py 2018-05-09 04:35:23.000000000 -0500 +@@ -31,7 +31,7 @@ class Record(object): + + def wl_increment(self): + # overflow prevention +- if self.wl_count < sys.maxint: ++ if self.wl_count < sys.maxsize: + self.wl_count += 1 + if self.wl_entered is None: + self.wl_entered = datetime.datetime.now() +@@ -39,7 +39,7 @@ class Record(object): + + def r_increment(self): + # overflow prevention +- if self.r_count < sys.maxint: ++ if self.r_count < sys.maxsize: + self.r_count += 1 + if self.r_entered is None: + self.r_entered = datetime.datetime.now() +diff -puriN pyzor-release-1-0-0/pyzor/engines/gdbm_.py pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/pyzor/engines/gdbm_.py +--- pyzor-release-1-0-0/pyzor/engines/gdbm_.py 2014-12-10 07:48:21.000000000 -0600 ++++ pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/pyzor/engines/gdbm_.py 2018-05-09 04:35:23.000000000 -0500 +@@ -75,7 +75,7 @@ class GdbmDBHandle(BaseEngine): + def apply_method(self, method, varargs=(), kwargs=None): + if kwargs is None: + kwargs = {} +- return apply(method, varargs, kwargs) ++ return method(*varargs, **kwargs) + + def __getitem__(self, key): + return self.apply_method(self._really_getitem, (key,)) +diff -puriN pyzor-release-1-0-0/pyzor/engines/mysql.py pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/pyzor/engines/mysql.py +--- pyzor-release-1-0-0/pyzor/engines/mysql.py 2014-12-10 07:48:21.000000000 -0600 ++++ pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/pyzor/engines/mysql.py 2018-05-09 04:35:23.000000000 -0500 +@@ -162,7 +162,7 @@ class MySQLDBHandle(BaseEngine): + "VALUES (%%s, 1, 0, NOW(), NOW(), NOW(), NOW()) ON " + "DUPLICATE KEY UPDATE r_count=r_count+1, " + "r_updated=NOW()" % self.table_name, +- itertools.imap(lambda key: (key,), keys)) ++ map(lambda key: (key,), keys)) + finally: + c.close() + +@@ -174,7 +174,7 @@ class MySQLDBHandle(BaseEngine): + "VALUES (%%s, 0, 1, NOW(), NOW(), NOW(), NOW()) ON " + "DUPLICATE KEY UPDATE wl_count=wl_count+1, " + "wl_updated=NOW()" % self.table_name, +- itertools.imap(lambda key: (key,), keys)) ++ map(lambda key: (key,), keys)) + finally: + c.close() + +@@ -294,7 +294,7 @@ class ThreadedMySQLDBHandle(MySQLDBHandl + def reconnect(self): + if not self.bound: + return +- for _ in xrange(self.bound): ++ for _ in range(self.bound): + self.db_queue.put(self._get_new_connection()) + + def _reconnect(self, db): +diff -puriN pyzor-release-1-0-0/pyzor/hacks/py3.py pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/pyzor/hacks/py3.py +--- pyzor-release-1-0-0/pyzor/hacks/py3.py 1969-12-31 18:00:00.000000000 -0600 ++++ pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/pyzor/hacks/py3.py 2018-05-09 04:35:23.000000000 -0500 +@@ -0,0 +1,21 @@ ++"""Hacks for python2-3 compatibility.""" ++ ++import sys ++ ++ ++def reload(module): ++ """Reload the modeule. ++ ++ This is handled differently according to the ++ python version. This even varies across Python3 ++ versions ++ """ ++ if sys.version_info[0] == 2: ++ # Built-in method ++ return reload(module) ++ elif sys.version_info[0] == 3 and sys.version_info[1] <= 3: ++ import imp ++ return imp.reload(module) ++ else: ++ import importlib ++ return importlib.reload(module) +diff -puriN pyzor-release-1-0-0/pyzor/server.py pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/pyzor/server.py +--- pyzor-release-1-0-0/pyzor/server.py 2014-12-10 07:48:21.000000000 -0600 ++++ pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/pyzor/server.py 2018-05-09 04:35:23.000000000 -0500 +@@ -137,8 +137,8 @@ class PreForkServer(Server): + def serve_forever(self, poll_interval=0.5): + """Fork the current process and wait for all children to finish.""" + pids = [] +- for dummy in xrange(self._prefork): +- database = self.database.next() ++ for dummy in range(self._prefork): ++ database = next(self.database) + pid = os.fork() + if not pid: + # Create the database in the child process, to prevent issues +@@ -312,7 +312,7 @@ class RequestHandler(SocketServer.Datagr + This command returns maxint for report counts and 0 whitelist. + """ + self.server.log.debug("Request pong for %s", digests[0]) +- self.response["Count"] = "%d" % sys.maxint ++ self.response["Count"] = "%d" % sys.maxsize + self.response["WL-Count"] = "%d" % 0 + + def handle_check(self, digests): +diff -puriN pyzor-release-1-0-0/requirements.txt pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/requirements.txt +--- pyzor-release-1-0-0/requirements.txt 2014-12-10 07:48:21.000000000 -0600 ++++ pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/requirements.txt 2018-05-09 04:35:23.000000000 -0500 +@@ -1,10 +1,10 @@ + # Depending on what engine type you want to use + # you will need one of the following +-MySQL-python==1.2.5 +-redis==2.9.1 ++mysqlclient==1.3.9 ++redis==2.10.5 + # python-gdbm # not available via pip + + # If you want to use gevent you will also require + # this. +-#gevent==1.0.1 ++#gevent==1.0.2 + +diff -puriN pyzor-release-1-0-0/scripts/pyzor pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/scripts/pyzor +--- pyzor-release-1-0-0/scripts/pyzor 2014-12-10 07:48:21.000000000 -0600 ++++ pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/scripts/pyzor 2018-05-09 04:35:23.000000000 -0500 +@@ -17,9 +17,9 @@ import tempfile + import threading + + try: +- import ConfigParser +-except ImportError: + import configparser as ConfigParser ++except ImportError: ++ import ConfigParser + + import pyzor.digest + import pyzor.client +@@ -110,7 +110,7 @@ def load_configuration(): + config = ConfigParser.ConfigParser() + # Set the defaults. + config.add_section("client") +- for key, value in defaults.iteritems(): ++ for key, value in defaults.items(): + config.set("client", key, value) + # Override with the configuration. + config.read(os.path.join(options.homedir, "config")) +@@ -176,9 +176,30 @@ def _get_input_msg(digester): + yield digested + + ++def _is_binary_reader(stream, default=False): ++ try: ++ return isinstance(stream.read(0), bytes) ++ except Exception: ++ return default ++ ++ ++def get_binary_stdin(): ++ # sys.stdin might or might not be binary in some extra cases. By ++ # default it's obviously non binary which is the core of the ++ # problem but the docs recommend changing it to binary for such ++ # cases so we need to deal with it. ++ is_binary = _is_binary_reader(sys.stdin, False) ++ if is_binary: ++ return sys.stdin ++ buf = getattr(sys.stdin, 'buffer', None) ++ if buf is not None and _is_binary_reader(buf, True): ++ return buf ++ raise RuntimeError('Did not manage to get binary stdin') ++ ++ + def _get_input_mbox(digester): + tfile = tempfile.NamedTemporaryFile() +- tfile.write(sys.stdin.read().encode("utf8")) ++ tfile.write(get_binary_stdin().read()) + tfile.seek(0) + mbox = mailbox.mbox(tfile.name) + for msg in mbox: +@@ -372,7 +393,7 @@ def genkey(client, servers, config, hash + return False + # pylint: disable-msg=W0612 + salt = "".join([chr(random.randint(0, 255)) +- for unused in xrange(hash_func(b"").digest_size)]) ++ for unused in range(hash_func(b"").digest_size)]) + if sys.version_info >= (3, 0): + salt = salt.encode("utf8") + salt_digest = hash_func(salt) +diff -puriN pyzor-release-1-0-0/scripts/pyzor-migrate pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/scripts/pyzor-migrate +--- pyzor-release-1-0-0/scripts/pyzor-migrate 2014-12-10 07:48:21.000000000 -0600 ++++ pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/scripts/pyzor-migrate 2018-05-09 04:35:23.000000000 -0500 +@@ -31,7 +31,7 @@ def migrate(options): + it = source_engine.iteritems() + while True: + try: +- key, record = it.next() ++ key, record = next(it) + destination_engine[key] = record + if options.delete: + del source_engine[key] +diff -puriN pyzor-release-1-0-0/scripts/pyzord pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/scripts/pyzord +--- pyzor-release-1-0-0/scripts/pyzord 2014-12-10 07:48:21.000000000 -0600 ++++ pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/scripts/pyzord 2018-05-09 04:35:23.000000000 -0500 +@@ -8,13 +8,17 @@ import os + import sys + import optparse + import traceback +-import ConfigParser ++try: ++ import configparser as ConfigParser ++except ImportError: ++ import ConfigParser + + import pyzor.client + import pyzor.config + import pyzor.server + import pyzor.engines + import pyzor.forwarder ++import pyzor.hacks.py3 + + + def detach(stdout="/dev/null", stderr=None, stdin="/dev/null", pidfile=None): +@@ -244,7 +248,7 @@ def load_configuration(): + config = ConfigParser.ConfigParser() + # Set the defaults. + config.add_section("server") +- for key, value in defaults.iteritems(): ++ for key, value in defaults.items(): + config.set("server", key, value) + # Override with the configuration. + config.read(os.path.join(options.homedir, "config")) +@@ -260,7 +264,7 @@ def main(): + """Run the pyzor daemon.""" + # Set umask - this restricts this process from granting any world access + # to files/directories created by this process. +- os.umask(0077) ++ os.umask(0o0077) + + config, options = load_configuration() + +@@ -338,7 +342,7 @@ def main(): + # Enssure that all modules are reloaded so they benefit from + # the gevent library. + for module in (os, sys, pyzor, pyzor.server, pyzor.engines): +- reload(module) ++ pyzor.hacks.py3.reload(module) + + if options.detach: + detach(stdout=options.detach, pidfile=pidfile_fn) +diff -puriN pyzor-release-1-0-0/scripts/run_tests pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/scripts/run_tests +--- pyzor-release-1-0-0/scripts/run_tests 2014-12-10 07:48:21.000000000 -0600 ++++ pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/scripts/run_tests 2018-05-09 04:35:23.000000000 -0500 +@@ -1,25 +1,36 @@ + #!/bin/bash +-set -ev ++set -ex + +-pip install pytest pytest-cov python-coveralls +- +-if [ `python -c 'import sys; print(sys.version_info[0])'` = '3' ] ++if [ "$1" == "clean" ] + then +- pip install redis +- python setup.py install +- 2to3 -w . ./scripts/pyzor ./scripts/pyzord ./scripts/pyzor-migrate ++ rm -rf build .cache dist .coverage pyzor.egg-info ++ mysql -e 'drop database if exists pyzor_test' ++ git reset --hard HEAD + else +- sudo apt-get update -qq +- sudo apt-get install -y build-essential python-dev libmysqlclient-dev +- pip install -r requirements.txt +- python setup.py install +- mysql -e 'create database pyzor_test' +-fi ++ sudo apt-get update -qq + +-if [ "$1" != "prepare" ] +-then +- py.test tests/unit/ --cov pyzor --cov-report term-missing +- py.test tests/functional/ +-fi ++ if [ `python -c 'import sys; print(sys.version_info[0])'` = '3' ] ++ then ++ sudo apt-get install -y python3-dev ++ else ++ pip install mock ++ sudo apt-get install -y python-dev ++ fi ++ ++ mysql --version | grep -qi maria && sudo apt-get install -y libmariadbclient-dev ++ mysql --version | grep -qi maria || sudo apt-get install -y libmysqlclient-dev ++ ++ sudo apt-get install -y build-essential ++ ++ pip install pytest pytest-cov python-coveralls ++ pip install -r requirements.txt ++ python setup.py install ++ mysql -e 'create database if not exists pyzor_test' + + ++ if [ "$1" != "prepare" ] ++ then ++ py.test tests/unit/ --cov pyzor --cov-report term-missing ++ py.test tests/functional/ ++ fi ++fi +\ No newline at end of file +diff -puriN pyzor-release-1-0-0/scripts/summarise.py pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/scripts/summarise.py +--- pyzor-release-1-0-0/scripts/summarise.py 1969-12-31 18:00:00.000000000 -0600 ++++ pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/scripts/summarise.py 2018-05-09 04:35:23.000000000 -0500 +@@ -0,0 +1,226 @@ ++#! /usr/bin/env python ++# -*- coding: utf-8 -*- ++ ++"""Summarise Pyzor database. ++ ++Generate a summary of the current state of a Pyzor database. ++ ++This currently only works with a MySQL (or compatible) database. ++This can currently only output to a Slack channel. ++ ++There are extra requirements for this script: ++ ++ * click ++ * requests ++""" ++ ++import os ++import datetime ++import ConfigParser ++ ++import MySQLdb ++ ++import requests ++ ++import click ++ ++ ++@click.command() ++@click.option("--config", default=None) ++@click.argument("hook") ++def summarise(config, hook): ++ """Generate a summary of a Pyzor database.""" ++ if config is None: ++ config = os.path.expanduser("~/.pyzor/config") ++ conf = ConfigParser.ConfigParser() ++ conf.read(config) ++ (host, user, password, db_name, ++ table) = conf.get("server", "DigestDB").split(",") ++ db = MySQLdb.connect( ++ host=host, ++ user=user, ++ db=db_name, ++ passwd=password, ++ ) ++ c = db.cursor() ++ ++ # TODO: With a newer Python, this could use f-strings. ++ data = {} ++ c.execute( ++ "SELECT COUNT(*) FROM `%s`" % table ++ ) ++ data["total"] = c.fetchone()[0] ++ c.execute( ++ "SELECT MIN(wl_entered), MIN(wl_updated), " ++ "MIN(r_entered), MIN(r_updated), MAX(wl_entered), MAX(wl_updated), " ++ "MAX(r_entered), MAX(r_updated) from `%s`" % table ++ ) ++ (data["oldest_white"], data["oldest_white_update"], ++ data["oldest_spam"], data["oldest_spam_update"], ++ data["newest_white"], data["newest_white_update"], ++ data["newest_spam"], data["newest_spam_update"] ++ ) = c.fetchone() ++ c.execute( ++ "SELECT MAX(r_count), MAX(wl_count) FROM `%s`" % table ++ ) ++ data["max_spam"], data["max_white"] = c.fetchone() ++ ++ # Frequency table for counts. ++ for column in ("r_count", "wl_count"): ++ buckets = [] ++ for bucket in range(10): ++ low = bucket * 100 ++ high = (bucket + 1) * 100 ++ c.execute( ++ "SELECT COUNT(*) FROM `%s` WHERE %s BETWEEN %%s AND %%s" % ++ (table, column), (low, high) ++ ) ++ buckets.append(c.fetchone()[0]) ++ data[column] = buckets ++ ++ # Frequency table for age. ++ for column in ("r_updated", "wl_updated"): ++ buckets = [] ++ for bucket in range(10): ++ now = datetime.datetime.now() ++ low = now - datetime.timedelta(days=(bucket + 1) * 7) ++ high = now - datetime.timedelta(days=bucket * 7) ++ c.execute( ++ "SELECT COUNT(*) FROM `%s` WHERE %s BETWEEN %%s AND %%s" % ++ (table, column), (low, high) ++ ) ++ buckets.append(c.fetchone()[0]) ++ data[column] = buckets ++ ++ data["table"] = table ++ notify_slack(hook, data) ++ ++ c.close() ++ db.close() ++ ++ ++# Borrowed from https://raw.githubusercontent.com/kennethreitz/spark.py/master/spark.py ++def spark_string(ints, fit_min=False): ++ """Returns a spark string from given iterable of ints. ++ ++ Keyword Arguments: ++ fit_min: Matches the range of the sparkline to the input integers ++ rather than the default of zero. Useful for large numbers with ++ relatively small differences between the positions ++ """ ++ ticks = u" ▁▂▃▄▅▆▇█" ++ min_range = min(ints) if fit_min else 0 ++ step_range = max(ints) - min_range ++ step = (step_range / float(len(ticks) - 1)) or 1 ++ return u''.join(ticks[int(round((i - min_range) / step))] for i in ints) ++ ++ ++def notify_slack(hook, data): ++ """Send a notification containing a summary of a Pyzor database to a ++ Slack channel.""" ++ text = "Pyzor summary for _%(table)s_ (%(total)s digests)" % data ++ format = "%d %b %Y" ++ if data["max_spam"] < 100: ++ spam_colour = "danger" ++ else: ++ spam_colour = "good" ++ if data["max_white"] < 100: ++ white_colour = "danger" ++ else: ++ white_colour = "good" ++ if (datetime.datetime.now() - data["newest_spam_update"]).days > 2: ++ spam_age_colour = "danger" ++ else: ++ spam_age_colour = "good" ++ if (datetime.datetime.now() - data["newest_white_update"]).days > 2: ++ white_age_colour = "danger" ++ else: ++ white_age_colour = "good" ++ attachments = [ ++ { ++ "title": "Spam Reports", ++ "text": spark_string(data["r_count"], fit_min=True), ++ "fields": [ ++ { ++ "title": "Most common count", ++ "value": data["max_spam"], ++ "short": True, ++ }, ++ ], ++ "color": spam_colour, ++ }, ++ { ++ "title": "Whitelist Reports", ++ "text": spark_string(data["wl_count"], fit_min=True), ++ "fields": [ ++ { ++ "title": "Most common count", ++ "value": data["max_white"], ++ "short": True, ++ }, ++ ], ++ "color": white_colour, ++ }, ++ { ++ "title": "Spam Age", ++ "text": spark_string(data["r_updated"], fit_min=True), ++ "fields": [ ++ { ++ "title": "Oldest", ++ "value": data["oldest_spam"].strftime(format), ++ "short": True, ++ }, ++ { ++ "title": "Oldest Update", ++ "value": data["oldest_spam_update"].strftime(format), ++ "short": True, ++ }, ++ { ++ "title": "Latest", ++ "value": data["newest_spam"].strftime(format), ++ "short": True, ++ }, ++ { ++ "title": "Latest Update", ++ "value": data["newest_spam_update"].strftime(format), ++ "short": True, ++ }, ++ ], ++ "color": spam_age_colour, ++ }, ++ { ++ "title": "Whitelist Age", ++ "text": spark_string(data["wl_updated"], fit_min=True), ++ "fields": [ ++ { ++ "title": "Oldest", ++ "value": data["oldest_white"].strftime(format), ++ "short": True, ++ }, ++ { ++ "title": "Oldest Update", ++ "value": data["oldest_white_update"].strftime(format), ++ "short": True, ++ }, ++ { ++ "title": "Latest", ++ "value": data["newest_white"].strftime(format), ++ "short": True, ++ }, ++ { ++ "title": "Latest Update", ++ "value": data["newest_white_update"].strftime(format), ++ "short": True, ++ }, ++ ], ++ "color": white_age_colour, ++ }, ++ ] ++ response = requests.post( ++ hook, ++ json={"text": text, "attachments": attachments} ++ ) ++ ++ ++if __name__ == "__main__": ++ summarise() +diff -puriN pyzor-release-1-0-0/setup.py pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/setup.py +--- pyzor-release-1-0-0/setup.py 2014-12-10 07:48:21.000000000 -0600 ++++ pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/setup.py 2018-05-09 04:35:23.000000000 -0500 +@@ -4,16 +4,6 @@ import distutils.core + + import pyzor + +-try: +- # These automatically run 2to3 and modules and scripts while installing, +- # when ran under Python 3 +- from distutils.command.build_py import build_py_2to3 as build_py +- from distutils.command.build_scripts import \ +- build_scripts_2to3 as build_scripts +-except ImportError: +- from distutils.command.build_py import build_py +- from distutils.command.build_scripts import build_scripts +- + long_description = """ + Pyzor is spam-blocking networked system that uses spam signatures + to identify them. +@@ -36,27 +26,26 @@ classifiers = ["Operating System :: POSI + + "Development Status :: 5 - Production/Stable", + +- "License :: OSI Approved :: GNU General Public License v2 (GPLv2)", +-] +- +-distutils.core.setup(name='pyzor', +- version=pyzor.__version__, +- description='networked spam-signature detection', +- long_description=long_description, +- author='Frank J. Tobin', +- author_email='ftobin@neverending.org', +- license='GPL', +- platforms='POSIX', +- keywords='spam', +- url='http://www.pyzor.org/', +- scripts=['scripts/pyzor', 'scripts/pyzord', +- 'scripts/pyzor-migrate'], +- packages=['pyzor', +- 'pyzor.engines', +- 'pyzor.hacks'], +- classifiers=classifiers, +- test_suite="tests.suite", +- cmdclass={'build_py': build_py, +- 'build_scripts': build_scripts, +- }, ++ "License :: OSI Approved :: GNU General Public License v2 (" ++ "GPLv2)", ++ ] ++ ++distutils.core.setup( ++ name='pyzor', ++ version=pyzor.__version__, ++ description='networked spam-signature detection', ++ long_description=long_description, ++ author='Frank J. Tobin', ++ author_email='ftobin@neverending.org', ++ license='GPL', ++ platforms='POSIX', ++ keywords='spam', ++ url='http://www.pyzor.org/', ++ scripts=['scripts/pyzor', 'scripts/pyzord', ++ 'scripts/pyzor-migrate'], ++ packages=['pyzor', ++ 'pyzor.engines', ++ 'pyzor.hacks'], ++ classifiers=classifiers, ++ test_suite="tests.suite", + ) +diff -puriN pyzor-release-1-0-0/tests/benchmark/measure_server_response.py pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/tests/benchmark/measure_server_response.py +--- pyzor-release-1-0-0/tests/benchmark/measure_server_response.py 2014-12-10 07:48:21.000000000 -0600 ++++ pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/tests/benchmark/measure_server_response.py 2018-05-09 04:35:23.000000000 -0500 +@@ -16,7 +16,7 @@ import string + import random + import hashlib + import pyzor.client +-digest = "".join(random.choice(string.letters) for _ in xrange(50)) ++digest = "".join(random.choice(string.letters) for _ in range(50)) + digest = hashlib.sha1(digest).hexdigest() + client = pyzor.client.Client(timeout=%f) + """ +@@ -59,7 +59,7 @@ def measure_methods(methods, repeats, ti + + def json_handler(res): + fres = {} +- for method, result in res.iteritems(): ++ for method, result in res.items(): + fres[method] = {"runs": [], + "timeouts": result["timeouts"], + "totals": {}} +@@ -81,7 +81,7 @@ def json_handler(res): + + + def print_handler(res): +- for method, result in res.iteritems(): ++ for method, result in res.items(): + print "=" * 80 + print "Method: %s" % method + print "Timeouts: %s" % result["timeouts"] +@@ -121,7 +121,7 @@ def main(): + queue = Queue.Queue() + + threads = [] +- for dummy in xrange(options.threads): ++ for dummy in range(options.threads): + thread = threading.Thread(target=measure_methods, + args=(options.method, options.repeats, + options.timeout, server, queue)) +diff -puriN pyzor-release-1-0-0/tests/functional/test_data/test.mbx pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/tests/functional/test_data/test.mbx +--- pyzor-release-1-0-0/tests/functional/test_data/test.mbx 1969-12-31 18:00:00.000000000 -0600 ++++ pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/tests/functional/test_data/test.mbx 2018-05-09 04:35:23.000000000 -0500 +@@ -0,0 +1,512 @@ ++From newsletter@mail.deco.proteste.pt Fri Nov 13 12:18:18 2015 ++Return-path: ++Envelope-to: SERGIO@SERJUX.COM ++Delivery-date: Fri, 13 Nov 2015 12:18:18 +0000 ++Received: from mail.deco.proteste.pt ([192.86.55.215]:18947) by ++ host5.easyho.st with esmtp (Exim 4.86) (envelope-from ++ ) id 1ZxDIz-0015rl-Kf for ++ SERGIO@SERJUX.COM; Fri, 13 Nov 2015 12:18:18 +0000 ++DKIM-Signature: v=1; a=rsa-sha1; c=relaxed/relaxed; s=kb; ++ d=mail.deco.proteste.pt; ++ h=Date:From:Reply-To:Subject:To:Message-ID:List-Unsubscribe:Mime-Version:Content-Type:Content-Transfer-Encoding; ++ i=newsletter@mail.deco.proteste.pt; bh=E2o7K8OWhvOEksscYicrUf/G/9c=; ++ b=c6tgurCRqVin4/9ie/BlbUy6TJz6BW4O3I9u6ft6iFqPvXvcQtb5WOGnWblD7EznmZ3OIsVpoR7s ++ c80Rm9a5bjMLqRpmw9/lHjcc9Shs6pXNVDS7Yk8s18bSp8htescrJ9FXJDsyp44ih7XS3CfHr49u ++ EP4+1cxaEsnPVW/bfvM= ++Received: from ukmailout02.em.unica.net (10.140.36.17) by ++ mail.deco.proteste.pt id h8nage0u8qoe for ; Fri, 13 Nov ++ 2015 12:17:34 +0000 (envelope-from ++ ) ++DKIM-Signature: v=1; a=rsa-sha1; c=relaxed/relaxed; s=uk_rel_kb; ++ d=emsg.unica.net; ++ h=Date:From:Reply-To:Subject:To:Message-ID:List-Unsubscribe:Mime-Version:Content-Type:Content-Transfer-Encoding; ++ bh=E2o7K8OWhvOEksscYicrUf/G/9c=; ++ b=N1V5cIphR8FfKhLvHLTfKGyUgmGyRySLCVsqefSpmrMc3PJF5aoadp2XJ8IWvspLLs0j8tH0W5k8 ++ f4B+v0x0WT2DtYEU8UnkDHOQwZuY3pRmiKCgeGAYdTZ0vNvqcXzGCt/d9m8yal5TpCbWGbKHnKk6 ++ D01qQ/ulU6b3eprH4tY= ++Received: from ukmme02.em.unica.net (10.140.36.19) by ++ ukmailout02.em.unica.net id h8nads0u8qo1 for ; Fri, 13 ++ Nov 2015 12:17:29 +0000 (envelope-from ++ ) ++Date: Fri, 13 Nov 2015 12:17:29 +0000 ++From: "Newsletter DECO PROTESTE" ++Reply-To: "mailbox6047x770594" ++Subject: =?UTF-8?Q?Black_Friday:_todos_os_nossos_c?= ++ =?UTF-8?Q?omparadores_dispon=C3=ADveis_para_si?= ++To: ++x-emsg-mtaselection: prod_6_mail.deco.proteste.pt ++Message-ID: ++Feedback-ID: mail.deco.proteste.pt:10259-179f:emsg-x ++List-Unsubscribe: ++ ++Mime-Version: 1.0 ++Content-Type: text/html; charset=UTF-8 ++X-Spam-Status: No, score=1.0 ++X-Spam-Score: 10 ++X-Spam-Bar: + ++X-Ham-Report: Spam detection software, running on the system ++ "host5.easyho.st", has NOT identified this incoming email as spam. The ++ original message has been attached to this so you can view it or label ++ similar future email. If you have any questions, see root\@localhost for ++ details. Content preview: Comparadores abertos especialmente para si ++ visualizar verso on-line Ol, [...] Content analysis details: (1.0 ++ points, 5.0 required) pts rule name description ---- ++ ---------------------- -------------------------------------------------- ++ 0.0 URIBL_BLOCKED ADMINISTRATOR NOTICE: The query to URIBL was ++ blocked. See http://wiki.apache.org/spamassassin/DnsBlocklists#dnsbl-block ++ for more information. [URIs: proteste.pt] -0.0 RCVD_IN_MSPIKE_H2 RBL: ++ Average reputation (+2) [192.86.55.215 listed in wl.mailspike.net] -0.0 ++ SPF_HELO_PASS SPF: HELO matches SPF record -0.0 SPF_PASS ++ SPF: sender matches SPF record -0.3 RP_MATCHES_RCVD Envelope ++ sender domain matches handover relay domain 1.1 URI_HEX URI: ++ URI hostname has long hexadecimal sequence 1.5 KAM_MXURI URI: ++ URI begins with a mail exchange prefix, i.e. mx.[...] -1.9 BAYES_00 ++ BODY: Bayes spam probability is 0 to 1% [score: 0.0000] 0.0 ++ HTML_MESSAGE BODY: HTML included in message 0.7 MIME_HTML_ONLY ++ BODY: Message only has text/html MIME parts -0.1 DKIM_VALID_AU ++ Message has a valid DKIM or DK signature from author's domain 0.1 ++ DKIM_SIGNED Message has a DKIM or DK signature, not necessarily ++ valid -0.1 DKIM_VALID Message has at least one valid DKIM or DK ++ signature 0.0 T_REMOTE_IMAGE Message contains an external image ++X-Spam-Flag: NO ++X-From-Rewrite: unmodified, no actual sender determined from check mail ++ permissions ++X-Evolution-Source: 1303672545.7032.3@segulix ++Content-Transfer-Encoding: 8bit ++ ++ ++ ++ ++ ++ ++ ++ Comparadores abertos especialmente para si ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++ ++ ++
++

visualizar versão on-line

++
++ ++ ++ ++ ++ ++ ++ ++ ++ ++
++ ++
++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++
++

Olá,

++

Queremos que tenha uma sexta-feira 13 cheia de poupança!

++

Por isso, abrimos as portas dos nossos comparadores online a associados e não associados, sem exceção. Veja livremente os resultados dos testes, descubra os produtos com a melhor relação qualidade/preço e conheça as lojas mais baratas.

++
++ ++ ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++ ++ ++ ++
++ ++
++ ++
++
++
++ ++ ++ ++ ++ ++ ++
++ Consultar todos os comparadores ++
++ ++

 

++
++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++
++

Partilhe com os seus amigos

++     ++
++ ++ ++ ++ ++ ++ ++
++

2015 © DECO PROTESTE, todos os direitos reservados.

++ Política de Privacidade | ++ Política de Cookies ++
++ ++ ++ ++ ++ ++ ++ ++ ++
++

Se não quiser receber e-mails ou outras informações sobre esta campanha, por favor clique aqui

++
++
++ ++ ++ ++ ++ ++ ++ ++ +diff -puriN pyzor-release-1-0-0/tests/functional/test_digest.py pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/tests/functional/test_digest.py +--- pyzor-release-1-0-0/tests/functional/test_digest.py 2014-12-10 07:48:21.000000000 -0600 ++++ pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/tests/functional/test_digest.py 2018-05-09 04:35:23.000000000 -0500 +@@ -615,6 +615,83 @@ Y3NBHskuH4zNJYCK+HAGV6rQ/AOymav06PYB2cx1 + QmCC + --f46d040a62c49bb1c804f027e8cc--""" + ++ ++TEXT_ATTACHMENT_W_NULL = """MIME-Version: 1.0 ++Received: by 10.76.127.40 with HTTP; Fri, 17 Jan 2014 02:21:43 -0800 (PST) ++Date: Fri, 17 Jan 2014 12:21:43 +0200 ++Delivered-To: chirila.s.alexandru@gmail.com ++Message-ID: ++Subject: Test ++From: Alexandru Chirila ++To: Alexandru Chirila ++Content-Type: multipart/mixed; boundary=f46d040a62c49bb1c804f027e8cc ++ ++--f46d040a62c49bb1c804f027e8cc ++Content-Type: multipart/alternative; boundary=f46d040a62c49bb1c404f027e8ca ++ ++--f46d040a62c49bb1c404f027e8ca ++Content-Type: text/plain; charset=ISO-8859-1 ++ ++This is a test ma\x00iling ++--f46d040a62c49bb1c804f027e8cc--""" ++ ++TEXT_ATTACHMENT_W_MULTIPLE_NULLS = """MIME-Version: 1.0 ++Received: by 10.76.127.40 with HTTP; Fri, 17 Jan 2014 02:21:43 -0800 (PST) ++Date: Fri, 17 Jan 2014 12:21:43 +0200 ++Delivered-To: chirila.s.alexandru@gmail.com ++Message-ID: ++Subject: Test ++From: Alexandru Chirila ++To: Alexandru Chirila ++Content-Type: multipart/mixed; boundary=f46d040a62c49bb1c804f027e8cc ++ ++--f46d040a62c49bb1c804f027e8cc ++Content-Type: multipart/alternative; boundary=f46d040a62c49bb1c404f027e8ca ++ ++--f46d040a62c49bb1c404f027e8ca ++Content-Type: text/plain; charset=ISO-8859-1 ++ ++This is a test ma\x00\x00\x00iling ++--f46d040a62c49bb1c804f027e8cc--""" ++ ++TEXT_ATTACHMENT_W_SUBJECT_NULL = """MIME-Version: 1.0 ++Received: by 10.76.127.40 with HTTP; Fri, 17 Jan 2014 02:21:43 -0800 (PST) ++Date: Fri, 17 Jan 2014 12:21:43 +0200 ++Delivered-To: chirila.s.alexandru@gmail.com ++Message-ID: ++Subject: Te\x00\x00\x00st ++From: Alexandru Chirila ++To: Alexandru Chirila ++Content-Type: multipart/mixed; boundary=f46d040a62c49bb1c804f027e8cc ++ ++--f46d040a62c49bb1c804f027e8cc ++Content-Type: multipart/alternative; boundary=f46d040a62c49bb1c404f027e8ca ++ ++--f46d040a62c49bb1c404f027e8ca ++Content-Type: text/plain; charset=ISO-8859-1 ++ ++This is a test mailing ++--f46d040a62c49bb1c804f027e8cc--""" ++ ++TEXT_ATTACHMENT_W_CONTENTTYPE_NULL = """MIME-Version: 1.0 ++Received: by 10.76.127.40 with HTTP; Fri, 17 Jan 2014 02:21:43 -0800 (PST) ++Date: Fri, 17 Jan 2014 12:21:43 +0200 ++Delivered-To: chirila.s.alexandru@gmail.com ++Message-ID: ++Subject: Test ++From: Alexandru Chirila ++To: Alexandru Chirila ++Content-Type: multipart/mixed; boundary=f46d040a62c49bb1c804f027e8cc ++ ++--f46d040a62c49bb1c804f027e8cc ++Content-Type: multipart/alternative; boundary=f46d040a62c49bb1c404f027e8ca ++ ++--f46d040a62c49bb1c404f027e8ca ++Content-Type: text/plain; charset="iso-8859-1\x00\x00\x00" ++ ++This is a test mailing ++--f46d040a62c49bb1c804f027e8cc--""" ++ + class PyzorPreDigestTest(PyzorTestBase): + # we don't need the pyzord server to test this + @classmethod +@@ -791,12 +868,35 @@ Emailspam.Emailspam,alsoknownasjunkemail + self.assertEqual(res.decode("utf8"), + hashlib.sha1(expected).hexdigest().lower() + "\n") + +- def test_digest_attachemnt(self): ++ def test_digest_attachment(self): + expected = b"Thisisatestmailing" + res = self.check_pyzor("digest", None, input=TEXT_ATTACHMENT) + self.assertEqual(res.decode("utf8"), + hashlib.sha1(expected).hexdigest().lower() + "\n") + ++ def test_digest_attachment_w_null(self): ++ expected = b"Thisisatestmailing" ++ res = self.check_pyzor("digest", None, input=TEXT_ATTACHMENT_W_NULL) ++ self.assertEqual(res.decode("utf8"), ++ hashlib.sha1(expected).hexdigest().lower() + "\n") ++ ++ def test_digest_attachment_w_multiple_nulls(self): ++ expected = b"Thisisatestmailing" ++ res = self.check_pyzor("digest", None, input=TEXT_ATTACHMENT_W_MULTIPLE_NULLS) ++ self.assertEqual(res.decode("utf8"), ++ hashlib.sha1(expected).hexdigest().lower() + "\n") ++ ++ def test_digest_attachment_w_subject_null(self): ++ expected = b"Thisisatestmailing" ++ res = self.check_pyzor("digest", None, input=TEXT_ATTACHMENT_W_SUBJECT_NULL) ++ self.assertEqual(res.decode("utf8"), ++ hashlib.sha1(expected).hexdigest().lower() + "\n") ++ ++ def test_digest_attachment_w_contenttype_null(self): ++ expected = b"Thisisatestmailing" ++ res = self.check_pyzor("digest", None, input=TEXT_ATTACHMENT_W_CONTENTTYPE_NULL) ++ self.assertEqual(res.decode("utf8"), ++ hashlib.sha1(expected).hexdigest().lower() + "\n") + + ENCODING_TEST_EMAIL = """From nobody Tue Apr 1 13:18:54 2014 + Content-Type: multipart/related; +@@ -831,15 +931,15 @@ VGhpcyBpcyBhIHTpc3Qg4qXG + BAD_ENCODING = """From nobody Tue Apr 1 13:18:54 2014 + Content-Type: multipart/related; + boundary="===============0632694142025794937==" +-MIME-Version: 1.0 ++MIME-Version: 1.0\x00\x00\x00 + + This is a multi-part message in MIME format. + --===============0632694142025794937== +-Content-Type: text/plain; charset=ISO-8859-1Content-Transfer-Encoding: quoted-printable ++Content-Type: text/plain; charset=ISO-8859-1\x00\x00\x00Content-Transfer-Encoding: quoted-printable + + This is a test + +---===============0632694142025794937== ++--===============0632694142025794937\x00\x00\x00== + Content-Type: text/plain; charset=us-asciia + Content-Transfer-Encoding: quoted-printable + +diff -puriN pyzor-release-1-0-0/tests/functional/test_engines/test_mysql.py pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/tests/functional/test_engines/test_mysql.py +--- pyzor-release-1-0-0/tests/functional/test_engines/test_mysql.py 2014-12-10 07:48:21.000000000 -0600 ++++ pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/tests/functional/test_engines/test_mysql.py 2018-05-09 04:35:23.000000000 -0500 +@@ -1,5 +1,8 @@ + import unittest +-import ConfigParser ++try: ++ import configparser as ConfigParser ++except ImportError: ++ import ConfigParser + + from tests.util import * + +diff -puriN pyzor-release-1-0-0/tests/functional/test_pyzor.py pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/tests/functional/test_pyzor.py +--- pyzor-release-1-0-0/tests/functional/test_pyzor.py 2014-12-10 07:48:21.000000000 -0600 ++++ pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/tests/functional/test_pyzor.py 2018-05-09 04:35:23.000000000 -0500 +@@ -1,9 +1,13 @@ ++import io + import sys + import redis + import unittest + + from tests.util import * + ++MBOX_FILE_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "test_data", "test.mbx") ++ ++ + class PyzorScriptTest(PyzorTestBase): + password_file = None + access = """ALL : anonymous : allow +@@ -70,7 +74,7 @@ class PyzorScriptTest(PyzorTestBase): + input = "da39a3ee5e6b4b0d3255bfef95601890afd80700" + self.client_args["-s"] = "digests" + self.check_pyzor("pong", None, input=input, code=200, exit_code=0, +- counts=(sys.maxint, 0)) ++ counts=(sys.maxsize, 0)) + self.check_pyzor("check", None, input=input, code=200, exit_code=1, + counts=(0, 0)) + self.check_pyzor("report", None, input=input, code=200, exit_code=0) +@@ -92,9 +96,9 @@ class PyzorScriptTest(PyzorTestBase): + self.client_args["-s"] = "digests" + self.check_pyzor_multiple("pong", None, input=input3, exit_code=0, + code=[200, 200, 200], +- counts=[(sys.maxint, 0), +- (sys.maxint, 0), +- (sys.maxint, 0)]) ++ counts=[(sys.maxsize, 0), ++ (sys.maxsize, 0), ++ (sys.maxsize, 0)]) + self.check_pyzor_multiple("check", None, input=input3, exit_code=1, + code=[200, 200, 200], + counts=[(0, 0), (0, 0), (0, 0)]) +@@ -111,7 +115,7 @@ class PyzorScriptTest(PyzorTestBase): + input = "From MAILER-DAEMON Mon Jan 6 15:13:33 2014\n\nTest1 message 0 Test2\n\n" + self.client_args["-s"] = "mbox" + self.check_pyzor("pong", None, input=input, code=200, exit_code=0, +- counts=(sys.maxint, 0)) ++ counts=(sys.maxsize, 0)) + self.check_pyzor("check", None, input=input, code=200, exit_code=1, + counts=(0, 0)) + self.check_pyzor("report", None, input=input, code=200, exit_code=0) +@@ -123,6 +127,24 @@ class PyzorScriptTest(PyzorTestBase): + r = self.get_record(input, None) + self.assertEqual(r["Count"], "1") + self.assertEqual(r["WL-Count"], "1") ++ ++ def test_mbox_real(self): ++ with io.open(MBOX_FILE_PATH, 'rt', encoding='latin-1') as mbox_file: ++ input = mbox_file.read() ++ self.client_args["-s"] = "mbox" ++ self.check_pyzor("pong", None, input=input, code=200, exit_code=0, ++ counts=(sys.maxsize, 0)) ++ self.check_pyzor("check", None, input=input, code=200, exit_code=1, ++ counts=(0, 0)) ++ self.check_pyzor("report", None, input=input, code=200, exit_code=0) ++ self.check_pyzor("check", None, input=input, code=200, exit_code=0, ++ counts=(1, 0)) ++ self.check_pyzor("whitelist", None, input=input, code=200, exit_code=0) ++ self.check_pyzor("check", None, input=input, code=200, exit_code=1, ++ counts=(1, 1)) ++ r = self.get_record(input, None) ++ self.assertEqual(r["Count"], "1") ++ self.assertEqual(r["WL-Count"], "1") + + def test_mbox_style_multiple(self): + input2 = "From MAILER-DAEMON Mon Jan 6 15:08:02 2014\n\nTest1 message 1 Test2\n\n"\ +@@ -133,9 +155,9 @@ class PyzorScriptTest(PyzorTestBase): + self.client_args["-s"] = "mbox" + self.check_pyzor_multiple("pong", None, input=input3, exit_code=0, + code=[200, 200, 200], +- counts=[(sys.maxint, 0), +- (sys.maxint, 0), +- (sys.maxint, 0)]) ++ counts=[(sys.maxsize, 0), ++ (sys.maxsize, 0), ++ (sys.maxsize, 0)]) + self.check_pyzor_multiple("check", None, input=input3, exit_code=1, + code=[200, 200, 200], + counts=[(0, 0), (0, 0), (0, 0)]) +@@ -174,9 +196,9 @@ class MultipleServerPyzorScriptTest(Pyzo + input = "Test1 multiple pong Test2" + self.check_pyzor_multiple("pong", None, input=input, exit_code=0, + code=[200, 200, 200], +- counts=[(sys.maxint, 0), +- (sys.maxint, 0), +- (sys.maxint, 0)]) ++ counts=[(sys.maxsize, 0), ++ (sys.maxsize, 0), ++ (sys.maxsize, 0)]) + + def test_check(self): + input = "Test1 multiple check Test2" +diff -puriN pyzor-release-1-0-0/tests/functional/test_server.py pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/tests/functional/test_server.py +--- pyzor-release-1-0-0/tests/functional/test_server.py 2014-12-10 07:48:21.000000000 -0600 ++++ pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/tests/functional/test_server.py 2018-05-09 04:35:23.000000000 -0500 +@@ -2,7 +2,10 @@ import sys + import time + import errno + import unittest +-import ConfigParser ++try: ++ import configparser as ConfigParser ++except ImportError: ++ import ConfigParser + + import pyzor.client + +diff -puriN pyzor-release-1-0-0/tests/unit/test_account.py pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/tests/unit/test_account.py +--- pyzor-release-1-0-0/tests/unit/test_account.py 2014-12-10 07:48:21.000000000 -0600 ++++ pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/tests/unit/test_account.py 2018-05-09 04:35:23.000000000 -0500 +@@ -1,19 +1,18 @@ +-"""Test the pyzor.account module +-""" ++"""Test the pyzor.account module""" ++ ++import io + import os +-import sys + import time + import email + import hashlib + import unittest +-import StringIO + + import pyzor + import pyzor.config + import pyzor.account + +-class AccountTest(unittest.TestCase): +- ++ ++class AccountTest(unittest.TestCase): + def setUp(self): + unittest.TestCase.setUp(self) + self.timestamp = 1381219396 +@@ -91,7 +90,7 @@ class LoadAccountTest(unittest.TestCase) + self.real_exists = os.path.exists + os.path.exists = lambda p: True if p == self.filepath else \ + self.real_exists(p) +- self.mock_file = StringIO.StringIO() ++ self.mock_file = io.StringIO() + try: + self.real_open = pyzor.account.__builtins__.open + except AttributeError: +@@ -121,8 +120,8 @@ class LoadAccountTest(unittest.TestCase) + + def test_load_accounts(self): + """Test loading the account file""" +- self.mock_file.write("public.pyzor.org : 24441 : test : 123abc,cba321\n" +- "public2.pyzor.org : 24441 : test2 : 123abc,cba321") ++ self.mock_file.write(u"public.pyzor.org : 24441 : test : 123abc,cba321\n" ++ u"public2.pyzor.org : 24441 : test2 : 123abc,cba321") + result = pyzor.config.load_accounts(self.filepath) + self.assertIn(("public.pyzor.org", 24441), result) + self.assertIn(("public2.pyzor.org", 24441), result) +@@ -135,8 +134,8 @@ class LoadAccountTest(unittest.TestCase) + + def test_load_accounts_invalid_line(self): + """Test loading the account file""" +- self.mock_file.write("public.pyzor.org : 24441 ; test : 123abc,cba321\n" +- "public2.pyzor.org : 24441 : test2 : 123abc,cba321") ++ self.mock_file.write(u"public.pyzor.org : 24441 ; test : 123abc,cba321\n" ++ u"public2.pyzor.org : 24441 : test2 : 123abc,cba321") + result = pyzor.config.load_accounts(self.filepath) + self.assertNotIn(("public.pyzor.org", 24441), result) + self.assertEquals(len(result), 1) +@@ -147,8 +146,8 @@ class LoadAccountTest(unittest.TestCase) + + def test_load_accounts_invalid_port(self): + """Test loading the account file""" +- self.mock_file.write("public.pyzor.org : a4441 : test : 123abc,cba321\n" +- "public2.pyzor.org : 24441 : test2 : 123abc,cba321") ++ self.mock_file.write(u"public.pyzor.org : a4441 : test : 123abc,cba321\n" ++ u"public2.pyzor.org : 24441 : test2 : 123abc,cba321") + result = pyzor.config.load_accounts(self.filepath) + self.assertNotIn(("public.pyzor.org", 24441), result) + self.assertEquals(len(result), 1) +@@ -159,8 +158,8 @@ class LoadAccountTest(unittest.TestCase) + + def test_load_accounts_invalid_key(self): + """Test loading the account file""" +- self.mock_file.write("public.pyzor.org : 24441 : test : ,\n" +- "public2.pyzor.org : 24441 : test2 : 123abc,cba321") ++ self.mock_file.write(u"public.pyzor.org : 24441 : test : ,\n" ++ u"public2.pyzor.org : 24441 : test2 : 123abc,cba321") + result = pyzor.config.load_accounts(self.filepath) + self.assertNotIn(("public.pyzor.org", 24441), result) + self.assertEquals(len(result), 1) +@@ -171,8 +170,8 @@ class LoadAccountTest(unittest.TestCase) + + def test_load_accounts_invalid_missing_comma(self): + """Test loading the account file""" +- self.mock_file.write("public.pyzor.org : 24441 : test : 123abccba321\n" +- "public2.pyzor.org : 24441 : test2 : 123abc,cba321") ++ self.mock_file.write(u"public.pyzor.org : 24441 : test : 123abccba321\n" ++ u"public2.pyzor.org : 24441 : test2 : 123abc,cba321") + result = pyzor.config.load_accounts(self.filepath) + self.assertNotIn(("public.pyzor.org", 24441), result) + self.assertEquals(len(result), 1) +@@ -183,7 +182,7 @@ class LoadAccountTest(unittest.TestCase) + + def test_load_accounts_comment(self): + """Test skipping commented lines""" +- self.mock_file.write("#public1.pyzor.org : 24441 : test : 123abc,cba321") ++ self.mock_file.write(u"#public1.pyzor.org : 24441 : test : 123abc,cba321") + result = pyzor.config.load_accounts(self.filepath) + self.assertNotIn(("public.pyzor.org", 24441), result) + self.assertFalse(result) +diff -puriN pyzor-release-1-0-0/tests/unit/test_config.py pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/tests/unit/test_config.py +--- pyzor-release-1-0-0/tests/unit/test_config.py 2014-12-10 07:48:21.000000000 -0600 ++++ pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/tests/unit/test_config.py 2018-05-09 04:35:23.000000000 -0500 +@@ -1,7 +1,10 @@ + import os + import logging + import unittest +-import ConfigParser ++try: ++ import configparser as ConfigParser ++except ImportError: ++ import ConfigParser + + try: + from unittest.mock import patch, Mock +@@ -260,7 +263,7 @@ class TestExpandHomeFiles(unittest.TestC + section = "test" + conf = ConfigParser.ConfigParser() + conf.add_section(section) +- for key, value in config.iteritems(): ++ for key, value in config.items(): + conf.set(section, key, value) + pyzor.config.expand_homefiles(homefiles, section, homedir, conf) + result = dict(conf.items(section)) +diff -puriN pyzor-release-1-0-0/tests/unit/test_digest.py pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/tests/unit/test_digest.py +--- pyzor-release-1-0-0/tests/unit/test_digest.py 2014-12-10 07:48:21.000000000 -0600 ++++ pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/tests/unit/test_digest.py 2018-05-09 04:35:23.000000000 -0500 +@@ -189,6 +189,18 @@ class DigestTests(unittest.TestCase): + + self.assertEqual(result, expected) + ++ def test_digest_w_null(self): ++ message = b"That's some good ham rig\x00ht there" ++ predigested = b"That'ssomegoodhamrightthere" ++ ++ digest = hashlib.sha1() ++ digest.update(predigested) ++ ++ expected = digest.hexdigest() ++ result = DataDigester(message).value ++ ++ self.assertEqual(result, expected) ++ + + class MessageDigestTest(unittest.TestCase): + def setUp(self): +diff -puriN pyzor-release-1-0-0/tests/unit/test_engines/test_gdbm.py pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/tests/unit/test_engines/test_gdbm.py +--- pyzor-release-1-0-0/tests/unit/test_engines/test_gdbm.py 2014-12-10 07:48:21.000000000 -0600 ++++ pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/tests/unit/test_engines/test_gdbm.py 2018-05-09 04:35:23.000000000 -0500 +@@ -25,7 +25,7 @@ class MockGdbmDB(dict): + if not self.keys(): + return None + self.key_index = 1 +- return self.keys()[0] ++ return list(self.keys())[0] + + def nextkey(self, key): + if len(self.keys()) <= self.key_index: +diff -puriN pyzor-release-1-0-0/tests/unit/test_forwarder.py pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/tests/unit/test_forwarder.py +--- pyzor-release-1-0-0/tests/unit/test_forwarder.py 2014-12-10 07:48:21.000000000 -0600 ++++ pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/tests/unit/test_forwarder.py 2018-05-09 04:35:23.000000000 -0500 +@@ -4,7 +4,10 @@ import time + import unittest + import threading + +-from mock import call, Mock ++try: ++ from unittest.mock import call, Mock ++except ImportError: ++ from mock import call, Mock + + import pyzor.forwarder + +@@ -23,7 +26,7 @@ class ForwarderTest(unittest.TestCase): + max_qsize = 10 + forwarder = pyzor.forwarder.Forwarder(client, servlist, + max_queue_size=max_qsize) +- for _ in xrange(max_qsize * 2): ++ for _ in range(max_qsize * 2): + forwarder.queue_forward_request('975422c090e7a43ab7c9bf0065d5b661259e6d74') + self.assertGreater(forwarder.forward_queue.qsize(), 0, 'queue insert failed') + self.assertLessEqual(forwarder.forward_queue.qsize(), max_qsize, 'queue overload') +diff -puriN pyzor-release-1-0-0/tests/unit/test_server.py pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/tests/unit/test_server.py +--- pyzor-release-1-0-0/tests/unit/test_server.py 2014-12-10 07:48:21.000000000 -0600 ++++ pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/tests/unit/test_server.py 2018-05-09 04:35:23.000000000 -0500 +@@ -5,7 +5,10 @@ import sys + import time + import logging + import unittest +-import SocketServer ++try: ++ import socketserver as SocketServer ++except ImportError: ++ import SocketServer + + from datetime import datetime, timedelta + +@@ -42,7 +45,7 @@ class MockDatagramRequestHandler(): + """ + self.rfile = io.BytesIO() + self.wfile = io.BytesIO() +- for i, j in headers.iteritems(): ++ for i, j in headers.items(): + self.rfile.write(("%s: %s\n" % (i, j)).encode("utf8")) + self.rfile.seek(0) + self.packet = None +@@ -129,7 +132,7 @@ class RequestHandlerTest(unittest.TestCa + self.request["Op"] = "pong" + self.request["Op-Digest"] = digest + handler = pyzor.server.RequestHandler(self.request, database) +- self.expected_response["Count"] = str(sys.maxint) ++ self.expected_response["Count"] = str(sys.maxsize) + self.expected_response["WL-Count"] = "0" + + self.check_response(handler) +diff -puriN pyzor-release-1-0-0/tests/util/__init__.py pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/tests/util/__init__.py +--- pyzor-release-1-0-0/tests/util/__init__.py 2014-12-10 07:48:21.000000000 -0600 ++++ pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/tests/util/__init__.py 2018-05-09 04:35:23.000000000 -0500 +@@ -117,7 +117,7 @@ class PyzorTestBase(unittest.TestCase): + cls.write_homedir_file("dan", cls.accounts_dan) + + args = ["pyzord"] +- for key, value in cls._args.iteritems(): ++ for key, value in cls._args.items(): + option = getattr(cls, key, None) + if option: + args.append(value) +@@ -163,7 +163,7 @@ class PyzorTestBase(unittest.TestCase): + if user: + args.append("--accounts-file") + args.append(user) +- for key, value in self.client_args.iteritems(): ++ for key, value in self.client_args.items(): + if value: + args.append(key) + args.append(value) +@@ -204,7 +204,7 @@ class PyzorTestBase(unittest.TestCase): + if user: + args.append("--accounts-file") + args.append(user) +- for key, value in self.client_args.iteritems(): ++ for key, value in self.client_args.items(): + if value: + args.append(key) + args.append(value) +@@ -284,7 +284,7 @@ class PyzorTest(object): + def test_pong(self): + input = "Test1 pong1 Test2" + self.check_pyzor("pong", "bob", input=input, code=200, exit_code=0, +- counts=(sys.maxint, 0)) ++ counts=(sys.maxsize, 0)) + + def test_check(self): + input = "Test1 check1 Test2" +diff -puriN pyzor-release-1-0-0/web/application.py pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/web/application.py +--- pyzor-release-1-0-0/web/application.py 2014-12-10 07:48:21.000000000 -0600 ++++ pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/web/application.py 2018-05-09 04:35:23.000000000 -0500 +@@ -7,10 +7,13 @@ import logging + import smtplib + import datetime + import email.utils +-import ConfigParser + import email.mime.base + import email.mime.text + import email.mime.multipart ++try: ++ import configparser as ConfigParser ++except ImportError: ++ import ConfigParser + + import flask + +@@ -74,9 +77,9 @@ def load_configuration(): + } + } + # Load in default values. +- for section, values in defaults.iteritems(): ++ for section, values in defaults.items(): + conf.add_section(section) +- for option, value in values.iteritems(): ++ for option, value in values.items(): + conf.set(section, option, value) + if os.path.exists("/etc/pyzor/web.conf"): + # Overwrite with local values. +diff -puriN pyzor-release-1-0-0/web/requirements.txt pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/web/requirements.txt +--- pyzor-release-1-0-0/web/requirements.txt 2014-12-10 07:48:21.000000000 -0600 ++++ pyzor-2b8d76d6b86ea2e9076a9c34ffd4651909d214b7/web/requirements.txt 2018-05-09 04:35:23.000000000 -0500 +@@ -1,4 +1,4 @@ +-flask==0.10.1 +-flask-wtf==0.9.5 +-raven==5.0.0 +-pyzor==0.8.0 ++flask==0.11.1 ++flask-wtf==0.13.1 ++raven==5.32.0 ++pyzor==1.0.0 diff --git a/mail-filter/pyzor/pyzor-9999.ebuild b/mail-filter/pyzor/pyzor-9999.ebuild new file mode 100644 index 0000000..c7abb73 --- /dev/null +++ b/mail-filter/pyzor/pyzor-9999.ebuild @@ -0,0 +1,69 @@ +# Copyright 1999-2020 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=6 + +PYTHON_COMPAT=( python3_6 python3_7 python3_8 ) +inherit distutils-r1 git-r3 + +DESCRIPTION="A distributed, collaborative spam detection and filtering network" +HOMEPAGE="https://github.com/SpamExperts/pyzor" +EGIT_REPO_URI="https://github.com/majn/telegram-purple" +EGIT_BRANCH="master" + +LICENSE="GPL-2" +SLOT="0" +KEYWORDS="~alpha ~amd64 ~hppa ~ia64 ~ppc ~ppc64 ~sparc ~x86 ~amd64-linux ~x86-linux" + +IUSE="doc gdbm gevent pyzord redis test" +# The test suite is py2-only +RESTRICT="test" + +# The mysql-python library is always required for the MySQL engine. We +# depend on it conditionally here because otherwise repoman will balk at +# the potential conflict between PYTHON_TARGETS and USE=mysql. But as a +# result, if you try to use the MySQL engine with python-3.x, it just +# won't work because you'll be missing the library. +RDEPEND=" + pyzord? ( + gdbm? ( $(python_gen_impl_dep 'gdbm') ) + redis? ( dev-python/redis-py[${PYTHON_USEDEP}] ) + gevent? ( dev-python/gevent[${PYTHON_USEDEP}] ) + )" +DEPEND="dev-python/setuptools[${PYTHON_USEDEP}] + doc? ( dev-python/sphinx[${PYTHON_USEDEP}] ) + test? ( ${RDEPEND} )" + +# TODO: maybe upstream would support skipping tests for which the +# dependencies are missing? +REQUIRED_USE="pyzord? ( || ( gdbm redis ) ) + test? ( gdbm redis )" + +#PATCHES=( +# "${FILESDIR}/65.patch" +#) + +python_test() { + PYTHONPATH=. "${PYTHON}" ./tests/unit/__init__.py +} + +python_compile_all() { + use doc && emake -C docs html +} + +python_install_all() { + use doc && HTML_DOCS=( docs/.build/html/. ) + distutils-r1_python_install_all +} + +src_install() { + distutils-r1_src_install + + if use pyzord; then + dodir /usr/sbin + mv "${D}"usr/bin/pyzord* "${ED}usr/sbin" \ + || die "failed to relocate pyzord" + else + rm "${D}"usr/bin/pyzord* || die "failed to remove pyzord" + fi +}