From 527e535279dcceeb6ffcf091bc9e8fc23b46f287 Mon Sep 17 00:00:00 2001 From: Matěj Cepl Date: Wed, 31 Dec 2014 12:12:02 +0100 Subject: Remove the spaghetti code from the main scripts. --- .gitignore | 1 + mail2news.py | 49 ++++++++++++----------- news2mail.py | 34 +++++++++------- pygm2n | 26 +++---------- pygn2m | 117 ++++++++++++++++++++++++------------------------------- setup.py | 1 + test/test_wlp.py | 18 ++++----- whitelist.py | 6 +-- 8 files changed, 114 insertions(+), 138 deletions(-) diff --git a/.gitignore b/.gitignore index f4366b8..19c0e79 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ wlp/lex.yy.c wlp.so pyg.egg-info/ dist/ +pyg.log diff --git a/mail2news.py b/mail2news.py index 7461eb8..9b3ffc9 100644 --- a/mail2news.py +++ b/mail2news.py @@ -14,12 +14,12 @@ Gets news email and sends it via SMTP. class mail2news is hopefully conform to rfc850. """ +from StringIO import StringIO from collections import OrderedDict import email import logging -import os import nntplib -from StringIO import StringIO +import os from re import findall from socket import gethostbyaddr, gethostname import sys @@ -36,7 +36,7 @@ DESC = "The Python Gateway Script: news2mail mail2news gateway" class mail2news(object): """news to mail gateway class""" - def __init__(self, verbose=False): + def __init__(self, options): # newsgroups = None # Newsgroups: local.test,local.moderated... # approved = None # Approved: kame@aragorn.lorien.org if 'NNTPHOST' in os.environ: @@ -47,34 +47,37 @@ class mail2news(object): self.port = 119 self.user = None self.password = None - self.verbose = verbose - logging.debug('self.verbose = %s', verbose) + self.verbose = options.verbose + logging.debug('self.verbose = %s', self.verbose) self.hostname = gethostbyaddr(gethostname())[0] self.heads_dict, self.smtpheads, self.nntpheads = {}, {}, {} - self.message = None + self.message = self.__readfile(options) - def add_header(self, header, value): - if value: - self.message[header] = value.strip() + self.message['X-Gateway'] = 'pyg {0} {1}'.format(VERSION, DESC) - def readfile(self, opt): + def __add_header(self, header, value, msg=None): + if msg is None: + msg = self.message + if value: + msg[header] = value.strip() - self.message = email.message_from_file(sys.stdin) + def __readfile(self, opt): + message = email.message_from_file(sys.stdin) - if (len(self.message) == 0) \ - and self.message.get_payload().startswith('/'): - msg_file_name = self.message.get_payload().strip() - del self.message + if (len(message) == 0) \ + and message.get_payload().startswith('/'): + msg_file_name = message.get_payload().strip() + del message with open(msg_file_name, 'r') as msg_file: - self.message = email.message_from_file(msg_file) + message = email.message_from_file(msg_file) # introduce nntpheads - self.add_header('Newsgroups', opt.newsgroup) - self.add_header('Approved', opt.approver) + self.__add_header('Newsgroups', opt.newsgroup, message) + self.__add_header('Approved', opt.approver, message) - return 1 + return message def renameheads(self): """rename headers such as Resent-*: to X-Resent-*: @@ -152,20 +155,20 @@ class mail2news(object): logging.debug('heads_dict = %s', heads_dict) for k in head_set: if k in heads_dict: - self.add_header(k, heads_dict[k]) + self.__add_header(k, heads_dict[k]) for k in heads_dict: if not k.startswith('X-') and not k.startswith('X-Resent-') \ and k not in head_set: - self.add_header(k, heads_dict[k]) + self.__add_header(k, heads_dict[k]) for k in heads_dict: if k.startswith('X-'): - self.add_header(k, heads_dict[k]) + self.__add_header(k, heads_dict[k]) for k in heads_dict: if k.startswith('X-Resent-'): - self.add_header(k, heads_dict[k]) + self.__add_header(k, heads_dict[k]) def sendemail(self): "Talk to NNTP server and try to send email." diff --git a/news2mail.py b/news2mail.py index 7338fd8..4248be2 100644 --- a/news2mail.py +++ b/news2mail.py @@ -24,13 +24,12 @@ normal (what pygs does) operations flow is: """ from collections import OrderedDict import email +from mail2news import VERSION, DESC import smtplib from socket import gethostbyaddr, gethostname import sys import time -from mail2news import VERSION, DESC - # logging.basicConfig(level=logging.DEBUG) class news2mail(object): @@ -39,6 +38,7 @@ class news2mail(object): def __init__(self, verbose=False): self.wlfile = None self.logfile = None + self.verbose = verbose self.sender = '' self.rcpt = '' @@ -50,15 +50,13 @@ class news2mail(object): self.heads_dict = {} self.article, self.headers, self.body = [], [], [] - self.message = email.message_from_file(sys.stdin) + self.message = self.__addheads(email.message_from_file(sys.stdin)) - self.verbose = verbose - - def addheads(self): + def __addheads(self, msg): """add new header like X-Gateway: Received: """ - self.message['X-Gateway'] = 'pyg {0} {1}'.format(VERSION, DESC) + msg['X-Gateway'] = 'pyg {0} {1}'.format(VERSION, DESC) # to make Received: header t = time.ctime(time.time()) @@ -79,10 +77,13 @@ class news2mail(object): '\n\tfor <' + self.rcpt + '> ; ' + \ t + ' (' + tzone + ')\n' - self.message['Received'] = tmp + msg['Received'] = tmp - def renameheads(self): - """rename headers such as Newsgroups: to X-Newsgroups: + return msg + + def __renameheads(self): + """remove headers like Xref: Path: Lines: + rename headers such as Newsgroups: to X-Newsgroups: headers renamed are useless or not rfc 822 copliant """ @@ -99,10 +100,6 @@ class news2mail(object): except KeyError as ex: print(ex) - def removeheads(self): - """remove headers like Xref: Path: Lines: - """ - try: # removing some others useless headers .... # that includes BOTH 'From ' and 'From' @@ -122,7 +119,7 @@ class news2mail(object): except KeyError, message: print message - def sortheads(self): + def __sortheads(self): """make list sorting heads, Received: From: To: Subject: first, others, X-*, Resent-* last""" @@ -150,6 +147,13 @@ class news2mail(object): if k.startswith('Resent-'): self.message[k] = heads_dict[k] + def process_message(self): + """phase 3: + format rfc 822 headers from input article + """ + self.__renameheads() # remove other heads + self.__sortheads() + def sendarticle(self): """Talk to SMTP server and try to send email.""" s = smtplib.SMTP(self.smtpserver) diff --git a/pygm2n b/pygm2n index ac37b77..3629857 100755 --- a/pygm2n +++ b/pygm2n @@ -15,17 +15,13 @@ Gets news article and sends it via SMTP. """ from __future__ import print_function -import sys -import nntplib -import logging -logging.basicConfig(level=logging.DEBUG) import argparse - +import logging import mail2news +import nntplib +import sys - -sys.path.append('/usr/lib/pyg') - +# logging.basicConfig(level=logging.DEBUG) def parse_cmdline(): parser = argparse.ArgumentParser( @@ -44,7 +40,6 @@ def parse_cmdline(): help='NNTP server password (for authentication)') parser.add_argument('-P', '--port', default='') parser.add_argument('-e', '--envellope', default='') - parser.add_argument('-w', '--wlfile') parser.add_argument('-l', '--logfile') parser.add_argument('-T', '--test', action='store_true', @@ -76,23 +71,12 @@ try: """ opt = parse_cmdline() - m2n = mail2news.mail2news(verbose=opt.verbose) + m2n = mail2news.mail2news(opt) owner = None - # reads stdin and parses article separating head from body - m2n.readfile(opt) - # m2n.parseemail() - - """phase 2: - check whitelist for user's permission - """ - """phase 3: format rfc 822 headers from input article """ - - m2n.message['X-Gateway'] = 'pyg {0} {1}'.format( - mail2news.VERSION, mail2news.DESC) m2n.renameheads() # rename useless heads m2n.removeheads() # remove other heads diff --git a/pygn2m b/pygn2m index aab8a20..a2c07cb 100755 --- a/pygn2m +++ b/pygn2m @@ -14,15 +14,13 @@ Thanks to md for this useful formula. Beer is beer. Gets news article from stdin and sends it via SMTP. """ from __future__ import print_function -import sys -import os -import argparse - -sys.path.append('/usr/lib/pyg') -import whitelist -import news2mail +import argparse from mail2news import VERSION, DESC +import news2mail +import os +import sys +import whitelist def parse_cmdline(): @@ -68,76 +66,61 @@ def parse_cmdline(): 4) open smtp connection and send e-mail """ -try: - - """phase 1: - check and set pyg's internal variables - """ - - # it returns only test, other parms are set directly in the actual - # parameter - args = parse_cmdline() - - n2m = news2mail.news2mail(verbose=args.verbose) - owner = None - - # check if n2m has some file prefercences set on commandline - if args.wlfile is None: - wl = os.path.expanduser(os.path.join(os.path.dirname(__file__), 'pyg.whitelist')) - else: - wl = args.wlfile - - if args.logfile is None: - log = os.path.expanduser(os.path.join(os.path.dirname(__file__), 'pyg.log')) - else: - log = args.logfile +"""phase 1: +check and set pyg's internal variables +""" -# print 'using %s %s\n' % (wl,log) +# it returns only test, other parms are set directly in the actual +# parameter +args = parse_cmdline() - wl = whitelist.whitelist(wl, log) +n2m = news2mail.news2mail(verbose=args.verbose) +owner = None - """phase 2: - check whitelist for user's permission - """ +# check if n2m has some file prefercences set on commandline +if args.wlfile is None: + wl = os.path.expanduser(os.path.join(os.path.dirname(__file__), 'pyg.whitelist')) +else: + wl = args.wlfile - # make a first check of From: address - 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])) - else: - wl.logmsg(n2m.nntpheads, wl.DENY) - - # if verbose, I want to print out headers, so I can't - # exit now. - if not args.verbose: - sys.exit(1) - - """phase 3: - format rfc 822 headers from input article - """ +if args.logfile is None: + log = os.path.expanduser(os.path.join(os.path.dirname(__file__), 'pyg.log')) +else: + log = args.logfile - n2m.addheads() # add some important heads - n2m.renameheads() # rename useless heads - n2m.removeheads() # remove other heads +wl = whitelist.whitelist(wl, log) - n2m.sortheads() # sort remaining heads :) +"""phase 2: +check whitelist for user's permission +""" - # prints formatted email message only (without send) if user wants - if args.verbose: - print(n2m.message.as_string()) +# make a first check of From: address +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])) + else: + wl.logmsg(n2m.nntpheads, wl.DENY) - if owner is None: + # if verbose, I want to print out headers, so I can't + # exit now. + if not args.verbose: sys.exit(1) - """phase 4: - open smtp connection and send e-mail - """ +# Reformat the message +n2m.process_message() - wl.logmsg(n2m.heads_dict, wl.ACCEPT, owner) - if not args.test: - n2m.sendarticle() +# prints formatted email message only (without send) if user wants +if args.verbose: + print(n2m.message.as_string()) -except KeyboardInterrupt: - print('Keyboard Interrupt') +if owner is None: sys.exit(1) + +"""phase 4: +open smtp connection and send e-mail +""" + +wl.logmsg(n2m.heads_dict, wl.ACCEPT, owner) +if not args.test: + n2m.sendarticle() diff --git a/setup.py b/setup.py index 53e77bc..716c101 100644 --- a/setup.py +++ b/setup.py @@ -25,6 +25,7 @@ class Build_WLP_ext(build_ext): 'Generating parser') build_ext.run(self) + def read(fname): return open(os.path.join(os.path.dirname(__file__), fname)).read() diff --git a/test/test_wlp.py b/test/test_wlp.py index 759dffe..4dd1516 100755 --- a/test/test_wlp.py +++ b/test/test_wlp.py @@ -1,14 +1,15 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -import unittest -import sysconfig -import sys +import mail2news import os import os.path -import subprocess import re -import mail2news +import subprocess +import sys +import sysconfig +import unittest +import wlp def distutils_dir_name(dname): @@ -21,7 +22,6 @@ wlp_lib_path = os.path.join('build', distutils_dir_name('lib')) sys.path.insert(0, wlp_lib_path) -import wlp class TestWLP(unittest.TestCase): @@ -61,7 +61,7 @@ one line test def test_m2n(self): with open('examples/mail') as in_mail: - pid = subprocess.Popen(['./pygm2n', '-TV', '-n', 'pyg.test'], + pid = subprocess.Popen(['./pygm2n', '-Tv', '-n', 'pyg.test'], stdin=subprocess.PIPE, stdout=subprocess.PIPE) out, _ = pid.communicate(in_mail.read()) @@ -93,8 +93,8 @@ Resent-Sender: sender@example.com env['PYTHONPATH'] = wlp_lib_path with open('examples/articletest.accepted') as in_mail: - pid = subprocess.Popen(['./pygn2m', '-TVt', 'test@example.com', - '-s', 'sender@example.com', '-d', + pid = subprocess.Popen(['./pygn2m', '-Tvt', 'test@example.com', + '-s', 'sender@example.com', '-w', 'examples/whitelist.example'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, env=env) diff --git a/whitelist.py b/whitelist.py index 8d941b9..d76af85 100644 --- a/whitelist.py +++ b/whitelist.py @@ -112,16 +112,16 @@ class whitelist(object): self.logf.write('\tMessage-ID: ' + heads.get('Message-ID')) else: self.logf.write('\tMessage-Id: ' + heads.get('Message-Id', - 'NOT PRESENT\n')) + 'NOT PRESENT\n')) # X-Newsgroups: and To: are present if user is trusted, else # Newsgroup: exists since no changes on nntp headers are done. if 'X-Newsgroups' in heads: self.logf.write('\tTo: ' + heads.get('To', 'NOT PRESENT\n')) self.logf.write('\tX-Newsgroups: ' + heads.get('X-Newsgroups', - 'NOT PRESENT\n')) + 'NOT PRESENT\n')) else: self.logf.write('\tNewsgroups: ' + - heads.get('Newsgroups', 'NOT PRESENT\n')) + heads.get('Newsgroups', 'NOT PRESENT\n')) self.logf.write('\n') -- cgit