diff options
-rw-r--r-- | mail2news.py | 178 | ||||
-rwxr-xr-x | pygm2n | 251 |
2 files changed, 175 insertions, 254 deletions
diff --git a/mail2news.py b/mail2news.py index d340432..3011a4e 100644 --- a/mail2news.py +++ b/mail2news.py @@ -3,7 +3,7 @@ Author: Cosimo Alfarano Date: September 16 2000 -mail2news.py - Copyright 2000 by Cosimo Alfarano <Alfarano@Students.CS.UniBo.It> +mail2news.py - (C) 2000 by Cosimo Alfarano <Alfarano@Students.CS.UniBo.It> You can use this software under the terms of the GPL. If we meet some day, and you think this stuff is worth it, you can buy me a beer in return. @@ -16,19 +16,17 @@ class mail2news is hopefully conform to rfc850. """ import sys -from os import unlink, getpid +from os import getpid from socket import gethostbyaddr, gethostname import string from re import findall -import time import nntplib import pyginfo -import tempfile + class mail2news: """news to mail gateway class""" - reader = None # mode reader # newsgroups = None # Newsgroups: local.test,local.moderated... # approved = None # Approved: kame@aragorn.lorien.org @@ -42,20 +40,25 @@ class mail2news: heads_dict, smtpheads, nntpheads = {}, {}, {} email, headers, body = [], [], [] - - def readfile(self): + def readfile(self, opt): for line in sys.stdin.readlines(): self.email.append(line) - + if(len(self.email) == 1 and self.email[0][0] == '/'): file = self.email[0][:-1] del self.email[0] - for line in open(file,'r').readlines(): + for line in open(file, 'r').readlines(): self.email.append(line) - - return 1 + # introduce nntpheads + if opt.newsgroup != '': + # TODO put it directly to self.message when we have it + self.nntpheads['Newsgroups:'] = opt.newsgroup + '\n' + if opt.approver != '': + self.nntpheads['Approved:'] = opt.approver + '\n' + + return 1 def parseemail(self): """get news email from file or stdin and separate heads from body @@ -66,7 +69,7 @@ class mail2news: try: body = 0 # are we in body or in headers? - + for line in self.email: if not body and len(line) == 1: body = 1 # starts email body section @@ -74,7 +77,7 @@ class mail2news: if not body: try: # if it is a multi-line header like Received: - if not line[0] in [' ','\t']: + if line[0] not in [' ', '\t']: try: head, value = string.split(line, ' ', 1) except string.index_error: @@ -84,17 +87,18 @@ class mail2news: self.smtpheads[head] = '%s%s' % \ (self.smtpheads[head], line) except (string.index_error), message: - print 'line: %s' % line - print '(probably missing couple "Header: value" in %s)' % line + print('line: %s' % line) + print('(probably missing couple "Header: value" in %s)' + % line) sys.exit(1) - + elif len(line) > 0 and body: self.body.append(line) - + except (string.index_error), message: print message sys.exit(1) - + return self.smtpheads, self.body def puthead(self, dict, list, key): @@ -102,7 +106,7 @@ class mail2news: Appends key of dict to list, deleting it from dict. """ - if dict.has_key(key): + if key in dict: list.append(key + ' ' + dict.get(key)) del dict[key] else: @@ -110,39 +114,42 @@ class mail2news: return 1 def sortheads(self): - """make list sorted by heads: From: To: Subject: first, others, X-*, X-Resent-* last""" + """make list sorted by heads: From: To: Subject: first, + others, X-*, X-Resent-* last""" - set = ('Newsgroups:','From:','To:','X-To:','Cc:','Subject:','Date:','Approved:','References:','Message-Id:') # put at top + # put at top + set = ('Newsgroups:', 'From:', 'To:', 'X-To:', 'Cc:', 'Subject:', + 'Date:', 'Approved:', 'References:', 'Message-Id:') for k in set: - self.puthead(self.heads_dict,self.headers,k) + self.puthead(self.heads_dict, self.headers, k) for k in self.heads_dict.keys(): if k[:2] != 'X-' and k[:9] != 'X-Resent-' and k not in set: - self.puthead(self.heads_dict,self.headers,k) + self.puthead(self.heads_dict, self.headers, k) for k in self.heads_dict.keys(): if k[:2] == 'X-': - self.puthead(self.heads_dict,self.headers,k) - + self.puthead(self.heads_dict, self.headers, k) + for k in self.heads_dict.keys(): if k[:9] == 'X-Resent-': - self.puthead(self.heads_dict,self.headers,k) - + self.puthead(self.heads_dict, self.headers, k) + return self.headers - - + def mergeheads(self): - """make a unique headers dictionary from NNTP and SMTP + """make a unique headers dictionary from NNTP and SMTP single headers dictionaries.""" - + self.heads_dict = {} try: - for header in self.smtpheads.keys(): # fill it w/ smtp old heads + for header in self.smtpheads.keys(): # fill it w/ smtp old heads self.heads_dict[header] = self.smtpheads[header] - - for header in self.nntpheads.keys(): # and replace them w/ nntp new heads + + # and replace them w/ nntp new heads + for header in self.nntpheads.keys(): self.heads_dict[header] = self.nntpheads[header] except KeyError, message: @@ -151,7 +158,7 @@ class mail2news: return self.heads_dict def addheads(self): - """add new header like X-Gateway: + """add new header like X-Gateway: """ info = pyginfo.pygsinfo() @@ -163,10 +170,10 @@ class mail2news: # it is nntpheads stuff # if(self.newsgroups): # self.heads_dict['Newsgroups:'] = self.newsgroups - + # if(self.approved): # self.heads_dict['Approved:'] = self.approved - + except KeyError, message: print message @@ -180,45 +187,46 @@ class mail2news: """ try: -### test -# if(post): -# if(self.heads_dict.has_key(post)): -# self.heads_dict['X-Original-' + post] = self.heads_dict[post] +### test +# if(post): +# if(post in self.heads_dict): +# self.heads_dict['X-Original-' + post] = self.heads_dict[post] # -# self.heads_dict[post] = self.heads_dict[pre] -# del(self.heads_dict[pre]) -# -# else: -# if(pre[0:2] == 'X-' and self.heads_dict.has_key(pre)): -# self.heads_dict['X-Original-' + pre] = self.heads_dict[pre] -# elif(not pre[0:2] == 'X-' and self.heads_dict.has_key('X-' + pre)): -# self.heads_dict['X-' + pre] = self.heads_dict[pre] -# del(self.heads_dict[pre]) +# self.heads_dict[post] = self.heads_dict[pre] +# del(self.heads_dict[pre]) +# +# else: +# if(pre[0:2] == 'X-' and pre in self.heads_dict): +# self.heads_dict['X-Original-' + pre] = self.heads_dict[pre] +# elif(not pre[0:2] == 'X-' and 'X-' + pre in self.heads_dict): +# self.heads_dict['X-' + pre] = self.heads_dict[pre] +# del(self.heads_dict[pre]) ### end test for key in self.heads_dict.keys(): if(key[:7] in ['Resent-']): - if(self.heads_dict.has_key('X-' + key)): - self.heads_dict[ 'X-Original-' + key ] = self.heads_dict['X-' + key] - self.heads_dict[ 'X-' + key ] = self.heads_dict[key] + if ('X-' + key) in self.heads_dict: + self.heads_dict['X-Original-' + key] = \ + self.heads_dict['X-' + key] + self.heads_dict['X-' + key] = self.heads_dict[key] del self.heads_dict[key] - # In rfc822 References: is considered, but many MUA doen't put it. - if(not self.heads_dict.has_key('References:') and self.heads_dict.has_key('In-Reply-To:')): + if ('References:' not in self.heads_dict) and \ + ('In-Reply-To:' in self.heads_dict): print self.heads_dict['In-Reply-To:'] # some MUA uses msgid without '<' '>' # ref = findall('([^\s<>\']+@[^\s<>;:\']+)', \ # but I prefer use RFC standards - ref = findall('(<[^<>]+@[^<>]+>)', \ - self.heads_dict['In-Reply-To:']) + ref = findall('(<[^<>]+@[^<>]+>)', + self.heads_dict['In-Reply-To:']) # if found, keep first element that seems a Msg-ID. if(ref and len(ref)): - self.heads_dict['References:'] = '%s\n' % ref[0] - -# if(self.heads_dict.has_key('To:')): + self.heads_dict['References:'] = '%s\n' % ref[0] + +# if('To:' in self.heads_dict): # self.heads_dict['X-To:'] = self.heads_dict['To:'] # del self.heads_dict['To:'] @@ -227,48 +235,46 @@ class mail2news: return self.heads_dict - - def removeheads(self, heads = None): + def removeheads(self, heads=None): """remove headers like Xref: Path: Lines: """ try: # removing some others useless headers .... (From is not From:) - rmheads = ['Received:', 'From', 'NNTP-Posting-Host:', \ - 'X-Trace:', 'X-Compliants-To:', 'NNTP-Posting-Date:'] + rmheads = ['Received:', 'From', 'NNTP-Posting-Host:', + 'X-Trace:', 'X-Compliants-To:', 'NNTP-Posting-Date:'] if(heads): rmheads.append(heads) - + for head in rmheads: - if self.heads_dict.has_key(head): + if head in self.heads_dict: del self.heads_dict[head] - -# if self.heads_dict.has_key('From'): # neither 'From ' nor 'From:' + +# if 'From' in self.heads_dict: # neither 'From ' nor 'From:' # del self.heads_dict['From'] -# if self.heads_dict.has_key('NNTP-Posting-Host:'): # neither 'From ' nor 'From:' +# # neither 'From ' nor 'From:' +# if 'NNTP-Posting-Host:' in self.heads_dict: # del self.heads_dict[''] - -# if self.heads_dict.has_key('Lines:'): +# if 'Lines:' in self.heads_dict: # del self.heads_dict['Lines:'] # it is usually set by INN, if ng is moderated... -# if self.heads_dict.has_key('Sender:'): +# if 'Sender:' in self.heads_dict: # del self.heads_dict['Sender:'] - - if self.heads_dict.has_key('Message-id:'): + if 'Message-id:' in self.heads_dict: self.heads_dict['Message-Id:'] = self.heads_dict['Message-id:'] del(self.heads_dict['Message-id:']) - - if self.heads_dict.has_key('Message-ID:'): + + if 'Message-ID:' in self.heads_dict: self.heads_dict['Message-Id:'] = self.heads_dict['Message-ID:'] del(self.heads_dict['Message-ID:']) - + # If message-id is not present, I generate it - if not self.heads_dict.has_key('Message-Id:'): + if 'Message-Id:' not in self.heads_dict: msgid = '<pyg.%d@tuchailepuppapera.org>\n' % (getpid()) self.heads_dict['Message-Id:'] = msgid @@ -280,19 +286,18 @@ class mail2news: def sendemail(self): """Talk to NNTP server and try to send email.""" try: - msglist = [] - - n = nntplib.NNTP(self.newsserver, self.port, self.user, self.password) + n = nntplib.NNTP(self.newsserver, self.port, self.user, + self.password) if(self.reader): n.putline('mode reader') resp = n.getline() print resp - + resp = n.shortcmd('POST') - # sett RFC977 2.4.2 - if resp[0] <> '3': + # sett RFC977 2.4.2 + if resp[0] != '3': raise n.error_reply, str(resp) for line in self.headers: @@ -316,5 +321,6 @@ class mail2news: n.putline('.') n.quit() return None - except (nntplib.error_reply, nntplib.error_temp, nntplib.error_perm, nntplib.error_proto, nntplib.error_data), message: - return 'NNTP: ' + str(message) + except (nntplib.error_reply, nntplib.error_temp, nntplib.error_perm, + nntplib.error_proto, nntplib.error_data), message: + return 'NNTP: ' + str(message) @@ -1,5 +1,4 @@ #!/usr/bin/env python - """News to mail gateway script. Copyright 2000 Cosimo Alfarano Author: Cosimo Alfarano @@ -13,200 +12,116 @@ Thanks to md for this useful formula. Beer is beer. Gets news article and sends it via SMTP. """ +from __future__ import print_function +import logging +import sys -import sys, os -import getopt -from string import split - -sys.path.append('/usr/lib/pyg') +import argparse +import mail2news import pyginfo -import mail2news - -def parse_cmdline(gw): - """Parses cmdline with getopt. and returns a dictionary with - smtp new header - """ - opt, arg = None, None - test, verbose = 0, 0 -# retnull = (None, None) - retnull = None - - retval = { 'test': 0, - 'verbose': 0 } - - try: - opt, arg = getopt.getopt(sys.argv[1:],"a:s:n:u:p:P:hvVTM") - except (getopt.error), message: - print '%s: %s\n' % (sys.argv[0], message) - sys.exit(1) - - if len(sys.argv) == 1 or opt == []: - gw.smtpheads = None - return retnull - - for i in range(len(opt)): - if opt[i][0] == '-h': - gw.nntpheads = None - return retnull - elif opt[i][0] == '-v': - gw.nntpheads = None - return retnull - elif opt[i][0] == '-n': - gw.nntpheads['Newsgroups:'] = opt[i][1] + '\n' - elif opt[i][0] == '-a': - gw.nntpheads['Approved:'] = opt[i][1] + '\n' - elif opt[i][0] == '-s': - gw.newsserver = opt[i][1] - elif opt[i][0] == '-P': - gw.port = int(opt[i][1]) -# elif opt[i][0] == '-d': -# gw.debug = 1 - elif opt[i][0] == '-u': - gw.user = opt[i][1] - elif opt[i][0] == '-p': - gw.password = opt[i][1] - elif opt[i][0] == '-T': - retval['test'] = 1 - elif opt[i][0] == '-V': - retval['verbose'] = 1 - elif opt[i][0] == '-M': - gw.reader = 1 - - if not gw.nntpheads.has_key('Newsgroups:'): - print 'Error: Missing Newsgroups\n' - return retnull - - -# 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 gw.rcpt == '' or gw.sender == '': -# print 'missing command line option' -# gw.smtpheads = None -# return retnull - -# if gw.envelope == '' and gw.sender != '': -# gw.smtpheads['Resent-From:'] = gw.sender + '\n' -# gw.envelope = gw.sender -# elif gw.envelope == -1: -# gw.smtpheads = None -# return retnull - - sys.argv[1:] = arg - -# return (test, verbose) - return retval - - -def usage(): - i = pyginfo.pygsinfo() - - print '%s version %s - Copyright 2000 Cosimo Alfarano' % (i.PROGNAME, i.VERSION) - print i.PROGDESC + ' - Mail to News' - print - print 'usage: %s -n newsgroup [-h] [-a approver] [-n newsgroup] [-T] [-V]' % split(sys.argv[0],'/')[-1] - print '-n newsgroup[s] (specified as comma separated without spaces list)' - print '-u user | -p passwword (for auth to newsserver)' - print '-a address of moderator/approver' - print '-s servername' -# print '-d for debug' - print '-h or -v for this info' - print '-T for test mode (not send article via NNTP)' - print '-V for verbose output (usefull with -T option for debugging)' +logging.basicConfig(format='%(levelname)s:%(funcName)s:%(message)s', + level=logging.DEBUG) -"""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 -""" +sys.path.append('/usr/lib/pyg') -try: +def parse_cmdline(): + i = pyginfo.pygsinfo() + parser = argparse.ArgumentParser( + description='%s version %s - Copyright 2000 Cosimo Alfarano\n%s' % + (i.PROGNAME, i.VERSION, i.__doc__)) + + 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='') + parser.add_argument('-p', '--password', default='', + help='password (for auth to newsserver)') + parser.add_argument('-P', '--port', default='') + parser.add_argument('-M', '--reader', default='') - """phase 1: - check and set pyg's internal variables - """ + parser.add_argument('-e', '--envellope', default='') + parser.add_argument('-t', '--to', dest='rcpt') + parser.add_argument('-w', '--wlfile') + parser.add_argument('-l', '--logfile') - m2n = mail2news.mail2news() - owner = None + parser.add_argument('-T', '--test', action='store_true', + help='test mode (not send article via NNTP)') + parser.add_argument('-d', '--debug', action='store_true') + parser.add_argument('-V', '--verbose', action='store_true', + help='verbose output ' + + '(usefull with -T option for debugging)') - opt = parse_cmdline(m2n) - if(opt == None): - usage() - sys.exit(0) + args = parser.parse_args() + if not args.newsgroup: + raise argparse.ArgumentError('Error: Missing Newsgroups\n') - # check if m2n has some file prefercences set on commandline -# if(m2n.wlfile == None): -# wl = os.environ['HOME'] + '/pyg.whitelist' -# else: -# wl = m2n.wlfile -# -# if(m2n.logfile == None): -# log = os.environ['HOME'] + '/pyg.log' -# else: -# log = m2n.logfile + logging.debug('args = %s', args) + return args -# wl = whitelist.whitelist(wl,log) - # reads stdin and parses article separating head from body - m2n.readfile() - m2n.parseemail() +"""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 +""" +try: -# for line in m2n.email: -# print line[:-1] + """phase 1: + check and set pyg's internal variables + """ -# for line in m2n.smtpheads.keys(): -# print line -# print m2n.smtpheads[line][:-1] + m2n = mail2news.mail2news() + owner = None - """phase 2: - check whitelist for user's permission - """ + opt = parse_cmdline() - # make a first check of From: address -# owner = wl.checkfrom(m2n.nntpheads['From:']) -# if(owner == None): -# if(sys.stdin.isatty()==1 or test or verbose): -# print ('"%s" is not in whitelist!' % (m2n.nntpheads['From:'][:-1])) -# else: -# wl.logmsg(m2n.nntpheads,wl.DENY) -# sys.exit(1) + # reads stdin and parses article separating head from body + m2n.readfile(opt) + m2n.parseemail() - """phase 3: - format rfc 822 headers from input article - """ + """phase 2: + check whitelist for user's permission + """ - m2n.mergeheads() # make unique dict from NNTP and SMTP dicts - - m2n.addheads() # add some important heads - m2n.renameheads() # rename useless heads - m2n.removeheads() # remove other heads + """phase 3: + format rfc 822 headers from input article + """ + + m2n.mergeheads() # make unique dict from NNTP and SMTP dicts - m2n.sortheads() # sort remaining heads :) + m2n.addheads() # add some important heads + m2n.renameheads() # rename useless heads + m2n.removeheads() # remove other heads - if(opt['verbose']): - for line in m2n.headers: - print line[:-1] + m2n.sortheads() # sort remaining heads :) - """phase 4: - open smtp connection and send e-mail - """ + if opt.verbose: + for line in m2n.headers: + print(line[:-1]) + + """phase 4: + open smtp connection and send e-mail + """ - if(len(m2n.headers) > 0 and len(m2n.body) > 0): -# wl.logmsg(m2n.heads_dict,wl.ACCEPT,owner) - if(not opt['test']): - resp = m2n.sendemail() - if resp: - print resp + if len(m2n.headers) > 0 and len(m2n.body) > 0: +# wl.logmsg(m2n.heads_dict,wl.ACCEPT,owner) + if not opt.test: + resp = m2n.sendemail() + if resp: + print(resp) except KeyboardInterrupt: - print 'Keyboard Interrupt' - sys.exit(0) + print('Keyboard Interrupt') + sys.exit(0) |