From d6b4d3860c4b36d35b9a8a96dcdf49093a139650 Mon Sep 17 00:00:00 2001 From: Matěj Cepl Date: Tue, 1 Oct 2019 14:44:02 +0200 Subject: A ways better. Tentatively fixes #7 --- mail2news.py | 40 ++++++++++++++++++++++++++-------------- news2mail.py | 44 ++++++++++++++++++++++++++++++-------------- setup.py | 1 - test/test_pyg.py | 19 +++++-------------- 4 files changed, 61 insertions(+), 43 deletions(-) diff --git a/mail2news.py b/mail2news.py index 386305b..dd4758c 100644 --- a/mail2news.py +++ b/mail2news.py @@ -15,7 +15,8 @@ Gets news email and sends it via SMTP. class mail2news is hopefully conform to rfc850. """ -from StringIO import StringIO +import argparse +from io import StringIO from collections import OrderedDict import email import logging @@ -54,7 +55,11 @@ class mail2news(object): self.hostname = gethostbyaddr(gethostname())[0] self.heads_dict, self.smtpheads, self.nntpheads = {}, {}, {} - self.message = self.__readfile(options) + if options.input == '': + self.message = self.__readfile(options, sys.stdin) + else: + with open(options.input, 'r') as inp_stream: + self.message = self.__readfile(options, inp_stream) self.message['X-Gateway'] = 'pyg {0} {1}'.format(VERSION, DESC) @@ -64,8 +69,8 @@ class mail2news(object): if value: msg[header] = value.strip() - def __readfile(self, opt): - message = email.message_from_file(sys.stdin) + def __readfile(self, opt, input_stream): + message = email.message_from_file(input_stream) if (len(message) == 0) \ and message.get_payload().startswith('/'): @@ -185,10 +190,10 @@ class mail2news(object): server.quit() -def parse_cmdline(): +def parse_cmdline(args): parser = argparse.ArgumentParser( description='%s version %s - Copyright 2000 Cosimo Alfarano\n%s' % - ('pyg', mail2news.VERSION, mail2news.DESC)) + ('pyg', VERSION, DESC)) parser.add_argument('-s', '--newsserver', default='') parser.add_argument('-a', '--approver', default='', @@ -201,7 +206,8 @@ def parse_cmdline(): parser.add_argument('-p', '--password', default='', help='NNTP server password (for authentication)') parser.add_argument('-P', '--port', default='') - parser.add_argument('-e', '--envellope', default='') + parser.add_argument('-e', '--envelope', default='') + parser.add_argument('-i', '--input', default='') parser.add_argument('-l', '--logfile') parser.add_argument('-T', '--test', action='store_true', @@ -210,7 +216,7 @@ def parse_cmdline(): help='verbose output ' + '(usefull with -T option for debugging)') - args = parser.parse_args() + args = parser.parse_args(args) if not args.newsgroup: raise argparse.ArgumentError('Error: Missing Newsgroups\n') @@ -218,21 +224,22 @@ def parse_cmdline(): return args -def main(): +def main(args=sys.argv[1:]): """main is structured in 4 phases: 1) check and set pyg's internal variables 2) check whitelist for users' permission 3) format rfc 822 headers from input article 4) open smtp connection and send e-mail """ + out = '' try: """phase 1: check and set pyg's internal variables """ - opt = parse_cmdline() + opt = parse_cmdline(args) - m2n = mail2news.mail2news(opt) + m2n = mail2news(opt) owner = None """phase 3: @@ -244,7 +251,7 @@ def main(): m2n.sortheads() # sort remaining heads :) if opt.verbose: - print(m2n.message.as_string()) + out += m2n.message.as_string() + '\n' logging.debug('m2n.payload = len %d', len(m2n.message.get_payload())) if len(m2n.message.get_payload()) > 0: @@ -253,7 +260,12 @@ def main(): try: resp = m2n.sendemail() except nntplib.NNTPError as ex: - print(ex) + logging.exception(ex) except KeyboardInterrupt: - print('Keyboard Interrupt') + logging.error('Keyboard Interrupt') sys.exit(0) + + if opt.input == '': + print(out) + else: + return out diff --git a/news2mail.py b/news2mail.py index eecd701..a21e1ed 100644 --- a/news2mail.py +++ b/news2mail.py @@ -23,23 +23,27 @@ normal (what pygs does) operations flow is: Date:, normal headers ending with X-* and Resent-* headers. """ -from collections import OrderedDict +import argparse +import os +import os.path +import collections import email from mail2news import VERSION, DESC import smtplib -from socket import gethostbyaddr, gethostname +import socket import sys import time +import whitelist # logging.basicConfig(level=logging.DEBUG) class news2mail(object): """news to mail gateway class""" - def __init__(self, verbose=False): + def __init__(self, options): self.wlfile = None self.logfile = None - self.verbose = verbose + self.verbose = options.verbose self.sender = '' self.rcpt = '' @@ -47,11 +51,16 @@ class news2mail(object): self.smtpserver = 'localhost' - self.hostname = gethostbyaddr(gethostname())[0] + self.hostname = socket.gethostbyaddr(socket.gethostname())[0] self.heads_dict = {} self.article, self.headers, self.body = [], [], [] - self.message = self.__addheads(email.message_from_file(sys.stdin)) + + if options.input == '': + self.message = self.__addheads(email.message_from_file(sys.stdin)) + else: + with open(options.input, 'r') as inp_stream: + self.message = self.__addheads(email.message_from_file(inp_stream)) def __addheads(self, msg): """add new header like X-Gateway: Received: @@ -132,7 +141,7 @@ class news2mail(object): # put at top header_set = ('Received', 'From', 'To', 'Subject', 'Date') - heads_dict = OrderedDict(self.message) + heads_dict = collections.OrderedDict(self.message) for hdr in self.message.keys(): del self.message[hdr] @@ -169,7 +178,7 @@ class news2mail(object): s.quit() -def parse_cmdline(): +def parse_cmdline(args): """ set a dictionary with smtp new header in gw parameter (gw.smtpheads) return (test,verbose) boolean tuple @@ -183,6 +192,7 @@ def parse_cmdline(): parser.add_argument('-e', '--envelope', default='') parser.add_argument('-t', '--to', dest='rcpt', required=True) parser.add_argument('-w', '--wlfile') + parser.add_argument('-i', '--input', default='') parser.add_argument('-l', '--logfile') parser.add_argument('-T', '--test', @@ -191,7 +201,7 @@ def parse_cmdline(): parser.add_argument('-v', '--verbose', help='verbose output', action='store_true') - opts = parser.parse_args() + opts = parser.parse_args(args) # By rfc822 [Resent-]Sender: should be ever set, unless == From: # (not this case). Should be a human, while [Resent-]From: may be a program. @@ -204,7 +214,7 @@ def parse_cmdline(): return opts -def main(): +def main(args=sys.argv[1:]): """main is structured in 4 phases: 1) check and set pyg's internal variables 2) check whitelist for users' permission @@ -215,12 +225,13 @@ def main(): """phase 1: check and set pyg's internal variables """ + out = '' # it returns only test, other parms are set directly in the actual # parameter - args = parse_cmdline() + args = parse_cmdline(args) - n2m = news2mail.news2mail(verbose=args.verbose) + n2m = news2mail(args) owner = None # check if n2m has some file prefercences set on commandline @@ -244,7 +255,7 @@ def main(): owner = wl.checkfrom(n2m.message['From']) if owner is None: if sys.stdin.isatty() == 1 or args.test: - print ('"%s" is not in whitelist!' % (n2m.message['From'][:-1])) + out += str('"%s" is not in whitelist!' % (n2m.message['From'][:-1])) + '\n' else: wl.logmsg(n2m.nntpheads, wl.DENY) @@ -258,7 +269,7 @@ def main(): # prints formatted email message only (without send) if user wants if args.verbose: - print(n2m.message.as_string()) + out += n2m.message.as_string() + '\n' if owner is None: sys.exit(1) @@ -270,3 +281,8 @@ def main(): wl.logmsg(n2m.heads_dict, wl.ACCEPT, owner) if not args.test: n2m.sendarticle() + + if args.input == '': + print(out) + else: + return out diff --git a/setup.py b/setup.py index 67e2574..53a6151 100644 --- a/setup.py +++ b/setup.py @@ -21,7 +21,6 @@ setup(name='pygn', py_modules=['mail2news', 'news2mail', 'setup', 'whitelist', 'wlp', 'wlp_parser'], test_suite="test", - scripts=['pygm2n', 'pygn2m'], entry_points={ "console_scripts": [ "pygm2n = mail2news:main", diff --git a/test/test_pyg.py b/test/test_pyg.py index e6b8fca..4076fc3 100755 --- a/test/test_pyg.py +++ b/test/test_pyg.py @@ -6,6 +6,7 @@ import subprocess import unittest import mail2news +import news2mail class TestM2N(unittest.TestCase): @@ -27,12 +28,7 @@ one line test """ % (mail2news.VERSION, mail2news.DESC) def test_m2n(self): - with open('examples/mail') as in_mail: - pid = subprocess.Popen(['python', 'pygm2n', '-Tv', '-n', 'pyg.test'], - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - universal_newlines=True) - out, _ = pid.communicate(in_mail.read()) + out = mail2news.main(['-T', '-v', '-i', 'examples/mail', '-n', 'pyg.test']) self.assertEqual(out, self.expected_output) @@ -59,22 +55,17 @@ Resent-Sender: sender@example.com def test_n2m(self): with open('examples/articletest.accepted') as in_mail: - pid = subprocess.Popen(['python', 'pygn2m', '-Tvt', 'test@example.com', - '-s', 'sender@example.com', - '-w', 'examples/whitelist.example'], - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - universal_newlines=True) in_message = in_mail.read().replace('pyg@pyg.server.tld', 'kame@inwind.it') - out, err = pid.communicate(in_message) + out = news2mail.main(['-T', '-v', '-t', 'test@example.com', + '-s', 'sender@example.com', + '-w', 'examples/whitelist.example']) out = re.sub(r'^Message-Id:.*$', '', out) # Not sure how to compare two email mesages (with different # times, etc.) so for now just to make sure the script doesn’t # blow up and gives some output # otherwise it would be # self.assertEqual(out, expected_output) - self.assertEqual(pid.returncode, 0) self.assertGreater(len(out), 0) -- cgit