updated nntpxmit.py file...

Lance Ellinghouse (lance@markv.com)
Fri, 13 Aug 93 8:50:33 PDT

I have fixed a number of crashes and dumps that I was not
expecting in the nntpxmit.py file I posted a while ago..
It has been up and running for almost 2 weeks straight now!
(that's about 2 weeks more than the original nntpxmit!)

Lance Ellinghouse
lance@markv.com
=====
#! /usr/local/bin/python

# This file reads the batch files created by NNTP and
# sends the messages to the remote site.

# The usage for this program is:
#
# nntpxmit Batch_dir News_dir

import sys
import strop
import posix
import socket
from nntplib import NNTP, error_temp, error_reply, error_perm

False = 0
True = 1

#####
# Global locations to use
#####
NewsSpoolDir = ''
NewsBatchDir = ''
if len(sys.argv) != 3:
print 'Usage: ' + sys.argv[0] + ' NNTP_Batch_Dir NEWS_Directory'
raise SystemExit
NewsSpoolDir = sys.argv[2] + '/'
NewsBatchDir = sys.argv[1] + '/'
NewsBatchHost= ''

#####
# Codes returned from NNTP Server
#####
OK_NOPOST = 201
OK_CANPOST = 200
OK_XFERD = 235
CONT_XFER = 335
ERR_GOTIT = 435
ERR_XFERRJCT = 437
ERR_XFERFAIL = 436

#####
# Globals
#####
# Transfer Stats
Stats = {
'offered':0,
'accepted':0,
'rejected':0,
'failed':0
}

Debug = False
Report_Stats = True
ReQueue_fails = True

Debug_Stream = None

Requeue_Article_lst = []

#####
# Support Functions
#####

def reset_stats():
global Stats
Stats['offered'] = 0
Stats['accepted'] = 0
Stats['rejected'] = 0
Stats['failed'] = 0
return None

def print_stats(host):
global Stats
print ''
print 'Statistics for \'' + host + '\''
print '='*30
print 'Offered : ' + `Stats['offered']`
print 'Accepted : ' + `Stats['accepted']`
print 'Rejected : ' + `Stats['rejected']`
print 'Failed : ' + `Stats['failed']`
print ''
return None

def write_requeue(file_name, lst):
try:
fp = open(NewsBatchDir + file_name,'a+')
except IOError, msg:
print 'write_requeue(' + NewsBatchDir + file_name + ') failed: ' + msg
return None
if len(lst) == 0:
fp.close()
return None
for item in lst:
try:
fp.write(item[0] + ' ' + item[1] + '\n')
except IOError, msg:
print 'write_requeue(' + NewsBatchDir + file_name + ') failed: ' + msg
return None
return None

# This routine takes a file name and loads all the lines in the file
# into a list. This list contains 2 parts, ('filename','message id')
# This information is taked from a batch file
def load_batch(file_name):
try:
fp = open(file_name,'r')
except:
return None
list = []
list = fp.readlines()
for ndx in range(len(list)):
list_el = strop.strip(list[ndx])
l = len(list_el)
j = 0
while j < l and list_el[j] != ' ':
j = j + 1
fn = list_el[0:j]
msgid = list_el[j:]
list[ndx] = (strop.strip(fn),strop.strip(msgid))
try:
t = posix.unlink(file_name)
except:
pass
return list

# this will lock a directory
def lock_dir(dir_to_lock):
t_dir = posix.getcwd()
r = posix.chdir(dir_to_lock)
try:
r = open('.LCK'+`posix.getpid()`,'w')
except:
r = posix.chdir(t_dir)
return False
r.close()
try:
r = posix.link('.LCK'+`posix.getpid()`,'.LCKdir')
except:
r = posix.chdir(t_dir)
return False
r = posix.chdir(t_dir)
return True

# This will unlock the directory
def unlock_dir(dir_to_unlock):
t_dir = posix.getcwd()
r = posix.chdir(dir_to_unlock)
try:
r = posix.unlink('.LCKdir')
r = posix.unlink('.LCK'+`posix.getpid()`)
except:
return False
r = posix.chdir(t_dir)
return True

# This function returns 2 lists..
# 1: A list of all files in the directory that are NOT requeue files (! '~xxx')
# 2: A list of all files in the directory that are requeue files (~xxx)
# This function NEVER returns files that start as '.LCK' as these
# are lock files and Never need to be touched and it never returns
# files that start with '.' as these are hidden
def rtn_dirlist(dir_to_list):
requeue_lst = []
rtn_lst = []
try:
lst = posix.listdir(dir_to_list)
except:
return (None, None)
for i in range(len(lst)):
name = lst[i]
if name[0] == '.':
continue
if name[0] == '~':
r = requeue_lst.append(name)
continue
rtn_lst.append(name)
return (rtn_lst, requeue_lst)

def process_file(f_name):
global Requeue_Article_lst
count = -1
lst = load_batch(NewsBatchDir + f_name)
if len(lst):
reset_stats()
try:
if f_name[0] == '~':
host = f_name[1:]
else:
host = f_name
print 'Connecting to ' + host
n = NNTP().init(host)
print 'NNTP welcomes with \'' + n.getwelcome() + '\''
except (EOFError,IOError,error_temp,error_perm,socket.error), msg:
print 'NNTP connect failed: ' + `msg`
for item in lst:
Requeue_Article_lst.append(item)
return None
for item in lst:
count = count + 1
try:
f = open(NewsSpoolDir + item[0],'r')
except IOError, msg:
print 'opening of ' + NewsSpoolDir + item[0],
print ' failed: ' + `msg`
Stats['failed'] = Stats['failed'] + 1
continue
try:
Stats['offered'] = Stats['offered'] + 1
rtn = n.ihave(item[1],f)
Stats['accepted'] = Stats['accepted'] + 1
except error_temp,msg:
rtn_val = eval(msg[:3])
if rtn_val == ERR_XFERFAIL:
print 'Transfer failed: Requeuing ' + item[1]
Requeue_Article_lst.append(item)
Stats['failed'] = Stats['failed'] + 1
if rtn_val == ERR_GOTIT:
Stats['rejected'] = Stats['rejected'] + 1
except EOFError, msg:
print 'Connection Closed!! requeueing items left'
print 'Returned: ' + `msg`
num_left = len(lst) - count
print 'Requeuing ' + `num_left` + ' items.'
for items in lst[count:]:
Requeue_Article_lst.append(items)
return None
n.quit()
print_stats(host)
return None

# This is the main routine that runs everything
def main():
global Stats
global Requeue_Article_lst
if not lock_dir(NewsBatchDir):
print 'Could not lock \'' + NewsBatchDir + '\'.'
print 'Try again later.'
raise SystemExit
do_list, redo_list = rtn_dirlist(NewsBatchDir)
if redo_list != None and len(redo_list):
for f_name in redo_list:
process_file(f_name)
if len(Requeue_Article_lst):
write_requeue(f_name,Requeue_Article_lst)
Requeue_Article_lst = []
if do_list != None and len(do_list):
for f_name in do_list:
process_file(f_name)
if len(Requeue_Article_lst):
write_requeue('~'+f_name,Requeue_Article_lst)
Requeue_Article_lst = []
r = unlock_dir(NewsBatchDir)
return None

main()