aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/mail2news.py98
-rw-r--r--src/news2mail.py118
-rwxr-xr-x[-rw-r--r--]test/test_pyg.py50
3 files changed, 234 insertions, 32 deletions
diff --git a/src/mail2news.py b/src/mail2news.py
index 6e8fe53..dc05c1d 100644
--- a/src/mail2news.py
+++ b/src/mail2news.py
@@ -14,6 +14,7 @@ Gets news email and sends it via SMTP.
class mail2news is hopefully conform to rfc850.
"""
+import argparse
import io
from collections import OrderedDict
import email
@@ -31,7 +32,7 @@ import tempfile
# This is the single source of Truth
# Yes, it is awkward to have it assymetrically here
# and not in news2mail as well.
-__version__ = '0.10.3'
+__version__ = '0.11.0'
__description__ = 'The Python Gateway Script: news2mail mail2news gateway'
@@ -55,9 +56,14 @@ 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)
+ self.message['X-Gateway'] = 'pyg {0} {1}'.format(__version__,
+ __description__)
def __add_header(self, header, value, msg=None):
if msg is None:
@@ -65,8 +71,8 @@ class mail2news(object):
if value:
msg[header] = value.strip()
- def __readfile(self, opt):
- message = email.message_from_file(sys.stdin, policy=email.policy.SMTP)
+ def __readfile(self, opt, input_stream):
+ message = email.message_from_file(input_stream, policy=email.policy.SMTP)
if (len(message) == 0) \
and message.get_payload().startswith('/'):
@@ -193,3 +199,85 @@ class mail2news(object):
logging.exception("Failed to convert message!")
server.quit()
+
+def parse_cmdline(args):
+ parser = argparse.ArgumentParser(
+ description='%s version %s - Copyright 2000 Cosimo Alfarano\n%s' %
+ ('pyg', __version__, __description__))
+
+ parser.add_argument('-s', '--newsserver', default='')
+ parser.add_argument('-a', '--approver', default='',
+ help="address of moderator/approver")
+ parser.add_argument('-n', '--newsgroup', default='',
+ help='newsgroup[s] (specified as comma separated ' +
+ 'without spaces list)', required=True)
+ parser.add_argument('-u', '--user', default='',
+ help='NNTP server user (for authentication)')
+ parser.add_argument('-p', '--password', default='',
+ help='NNTP server password (for authentication)')
+ parser.add_argument('-P', '--port', 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',
+ help='test mode (not send article via NNTP)')
+ parser.add_argument('-v', '--verbose', action='store_true',
+ help='verbose output ' +
+ '(usefull with -T option for debugging)')
+
+ args = parser.parse_args(args)
+
+ if not args.newsgroup:
+ raise argparse.ArgumentError('Error: Missing Newsgroups\n')
+
+ return args
+
+
+def main(args_in=None):
+ """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
+ """
+ if args_in is None:
+ args_in = sys.argv[1:]
+ opt = parse_cmdline(args_in)
+
+ m2n = mail2news(opt)
+ owner = None
+
+ """phase 3:
+ format rfc 822 headers from input article
+ """
+ m2n.renameheads() # rename useless heads
+ m2n.removeheads() # remove other heads
+
+ m2n.sortheads() # sort remaining heads :)
+
+ if opt.verbose:
+ out += m2n.message.as_string() + '\n'
+
+ logging.debug('m2n.payload = len %d', len(m2n.message.get_payload()))
+ if len(m2n.message.get_payload()) > 0:
+ # wl.logmsg(m2n.heads_dict,wl.ACCEPT,owner)
+ if not opt.test:
+ try:
+ resp = m2n.sendemail()
+ except nntplib.NNTPError as ex:
+ logging.exception(ex)
+ except KeyboardInterrupt:
+ logging.error('Keyboard Interrupt')
+ sys.exit(0)
+
+ if opt.input == '':
+ print(out)
+ else:
+ return out
diff --git a/src/news2mail.py b/src/news2mail.py
index 33d2590..f99b0d7 100644
--- a/src/news2mail.py
+++ b/src/news2mail.py
@@ -22,8 +22,7 @@ normal (what pygs does) operations flow is:
Date:, normal headers ending with X-* and Resent-* headers.
"""
-from __future__ import absolute_import
-from __future__ import print_function
+import argparse
from collections import OrderedDict
import email
import email.policy
@@ -31,7 +30,7 @@ import smtplib
from socket import gethostbyaddr, gethostname
import sys
import time
-from mail2news import VERSION, DESC
+import mail2news
# logging.basicConfig(level=logging.DEBUG)
@@ -60,7 +59,8 @@ class news2mail(object):
"""add new header like X-Gateway: Received:
"""
- msg['X-Gateway'] = 'pyg {0} {1}'.format(VERSION, DESC)
+ msg['X-Gateway'] = 'pyg {0} {1}'.format(mail2news.__version__,
+ mail2news.__description__)
# to make Received: header
t = time.ctime(time.time())
@@ -172,3 +172,113 @@ class news2mail(object):
s.quit()
+def parse_cmdline(a_in):
+ """
+ set a dictionary with smtp new header in gw parameter (gw.smtpheads)
+ return (test,verbose) boolean tuple
+ """
+ parser = argparse.ArgumentParser(
+ description='pyg version %s - Copyright 2000 Cosimo Alfarano\n%s' %
+ (mail2news.__version__, mail2news.__description__))
+
+ parser.add_argument('-H', '--smtpserver', default='')
+ parser.add_argument('-s', '--sender', required=True, default='')
+ 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',
+ help='test mode (not send article via SMTP)',
+ action='store_true')
+ parser.add_argument('-v', '--verbose', help='verbose output',
+ action='store_true')
+
+ opts = parser.parse_args(a_in)
+
+# By rfc822 [Resent-]Sender: should be ever set, unless == From:
+# (not this case). Should be a human, while [Resent-]From: may be a program.
+
+ if opts.rcpt == '' or opts.sender == '':
+ raise argparse.ArgumentError('missing command line option')
+
+ if opts.envelope == '' and opts.sender != '':
+ opts.envelope = opts.sender
+
+ return opts
+
+def main(args_in=None):
+ """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
+ """
+
+ """phase 1:
+ check and set pyg's internal variables
+ """
+ out = ''
+
+ # it returns only test, other parms are set directly in the actual
+ # parameter
+ if args_in is None:
+ args_in = sys.argv[1:]
+ args = parse_cmdline(args_in)
+
+ n2m = news2mail(args)
+ 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
+
+ wl = whitelist.whitelist(wl, log)
+
+ """phase 2:
+ check whitelist for user's permission
+ """
+
+ # 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:
+ out += str('"%s" is not in whitelist!' % (n2m.message['From'][:-1])) + '\n'
+ 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)
+
+ # Reformat the message
+ n2m.process_message()
+
+ # prints formatted email message only (without send) if user wants
+ if args.verbose:
+ out += n2m.message.as_string() + '\n'
+
+ 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()
+
+ if args.input == '':
+ print(out)
+ else:
+ return out
diff --git a/test/test_pyg.py b/test/test_pyg.py
index 6044520..6777bd2 100644..100755
--- a/test/test_pyg.py
+++ b/test/test_pyg.py
@@ -1,12 +1,10 @@
#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-from __future__ import absolute_import
import re
import subprocess
import unittest
import mail2news
+import news2mail
@unittest.skip('not ready')
@@ -26,15 +24,18 @@ X-Gateway: pyg %s %s
one line test
-""" % (mail2news.VERSION, mail2news.DESC)
+""" % (mail2news.__version__, mail2news.__description__)
def test_m2n(self):
- with open('examples/mail') as in_mail:
- pid = subprocess.Popen([sys.executable, 'src/mail2news.py', '-Tv', '-n', 'pyg.test'],
- stdin=subprocess.PIPE,
- stdout=subprocess.PIPE,
- universal_newlines=True)
- out, _ = pid.communicate(in_mail.read())
+ # with open('examples/mail') as in_mail:
+ # pid = subprocess.Popen([sys.executable,
+ # 'src/mail2news.py', '-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)
@@ -57,26 +58,29 @@ X-Gateway: pyg %s %s
X-NNTP-Posting-Host: pyg.server.tld
Resent-From: sender@example.com
Resent-Sender: sender@example.com
-""" % (mail2news.VERSION, mail2news.DESC)
+""" % (mail2news.__version__, mail2news.__description__)
def test_n2m(self):
- with open('examples/articletest.accepted') as in_mail:
- pid = subprocess.Popen([sys.executable, 'src/news2mail.py', '-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 = re.sub(r'^Message-Id:.*$', '', out)
+ # with open('examples/articletest.accepted') as in_mail:
+ # pid = subprocess.Popen([sys.executable, 'src/news2mail.py', '-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.assertEqual(pid.returncode, 0)
self.assertGreater(len(out), 0)