#!/usr/local/bin/python # Hey Emacs, this is -*-Python-*- code! # # Pipermail 0.0.2-mm # # **NOTE** # # This internal version of pipermail has been deprecated in favor of use of # an external version of pipermail, available from: # http://starship.skyport.net/crew/amk/maintained/pipermail.html # The external version should be pointed at the created archive files. # # # Some minor mods have been made for use with the Mailman mailing list manager. # All changes will have JV by them. # # (C) Copyright 1996, A.M. Kuchling (amk@magnet.com) # Home page at http://amarok.magnet.com/python/pipermail.html # # HTML code for frames courtesy of Scott Hassan (hassan@cs.stanford.edu) # # TODO: # * Prev. article, next. article pointers in each article # * I suspect there may be problems with rfc822.py's getdate() method; # take a look at the threads "Greenaway and the net (fwd)" or # "Pillow Book pictures". To be looked into... # * Anything else Hypermail can do that we can't? # * General code cleanups # * Profiling & optimization # * Should there be an option to enable/disable frames? # * Like any truly useful program, Pipermail should have an ILU interface. # * There's now an option to keep from preserving line breaks, # so paragraphs in messages would be reflowed by the browser. # Unfortunately, this mangles .sigs horribly, and pipermail doesn't yet # put in paragraph breaks. Putting in the breaks will only require a # half hour or so; I have no clue as to how to preserve .sigs. # * Outside URLs shouldn't appear in the display frame. How to fix? # VERSION = "0.0.2.mm" import posixpath, time, os, string, sys, rfc822 # JV -- to get HOME_PAGE import mm_cfg class ListDict: def __init__(self): self.dict={} def keys(self): return self.dict.keys() def __setitem__(self, key, value): "Add the value to a list for the key, creating the list if needed." if not self.dict.has_key(key): self.dict[key]=[value] else: self.dict[key].append(value) def __getitem__(self, key): "Return the list matching a key" return self.dict[key] def PrintUsage(): print """Pipermail %s usage: pipermail [options] options: -a URL : URL to other archives -b URL : URL to archive information -c file : name of configuration file (default: ~/.pmrc) -d dir : directory where the output files will be placed (default: archive/) -l name : name of the output archive -m file : name of input file -s file : name where the archive state is stored (default: +'.pipermail' -u : Select 'update' mode -v : verbose mode of operation """ % (VERSION,) sys.exit(0) # Compile various important regexp patterns import regex, regsub # Starting directive htmlpat=regex.compile('^[ \t]*[ \t]*$') # Ending directive nohtmlpat=regex.compile('^[ \t]*[ \t]*$') # Match quoted text quotedpat=regex.compile('^[>|:]+') # Parenthesized human name paren_name_pat=regex.compile('.*\([(].*[)]\).*') # Subject lines preceded with 'Re:' REpat=regex.compile('[ \t]*[Rr][Ee][ \t]*:[ \t]*') # Lines in the configuration file: set pm_XXX = cfg_line_pat=regex.compile('^[ \t]*[sS][eE][tT][ \t]*[Pp][Mm]_\([a-zA-Z0-9]*\)' '[ \t]*=[ \t]*\(.*\)[ \t\n]*$') # E-mail addresses and URLs in text emailpat=regex.compile('\([-+,.a-zA-Z0-9]*@[-+.a-zA-Z0-9]*\)') urlpat=regex.compile('\([a-zA-Z0-9]+://[^ \t\n]+\)') # URLs in text # Blank lines blankpat=regex.compile('^[ \t\n]*$') def ReadCfgFile(prefs): import posixpath try: f=open(posixpath.expanduser(prefs['CONFIGFILE']), 'r') except IOError, (num, msg): if num==2: return else: raise IOError, (num, msg) line=0 while(1): L=f.readline() ; line=line+1 if L=="": break if string.strip(L)=="": continue # Skip blank lines match=cfg_line_pat.match(L) if match==-1: print "Syntax error in line %i of %s" %(line, prefs['CONFIGFILE']) print L else: varname, value=cfg_line_pat.group(1,2) varname=string.upper(varname) if not prefs.has_key(varname): print ("Unknown variable name %s in line %i of %s" %(varname, line, prefs['CONFIGFILE'])) print L else: prefs[varname]=eval(value) f.close() def ReadEnvironment(prefs): import sys, os for key in prefs.keys(): envvar=string.upper('PM_'+key) if os.environ.has_key(envvar): if type(prefs[key])==type(''): prefs[key]=os.environ[envvar] else: prefs[key]=string.atoi(os.environ[envvar]) def UpdateMsgHeaders(prefs, filename, L): """Update the next/previous message information in a message header. The message is scanned for and comments, and new pointers are written. Otherwise, the text is simply copied without any processing.""" pass def ProcessMsgBody(prefs, msg, filename, articles): """Transform one mail message from plain text to HTML. This involves writing an HTML header, scanning through the text looking for directives, e-mail addresses, and URLs, and finishing off with a footer.""" import cgi, posixpath outputname=posixpath.join(prefs['DIR'], filename) output=open(outputname, 'w') os.chmod(outputname, prefs['FILEMODE']) subject, email, poster, date, datestr, parent, id = articles[filename] # JV if not email: email = '' if not subject: subject = '' if not poster: poster = '*Unknown*' if not datestr: datestr = '' output.write('' "%s Mailing List: %s" "

%s

" "%s (%s)
%s

" % (prefs['LABEL'], cgi.escape(subject),cgi.escape(subject), cgi.escape(poster),cgi.escape(email), cgi.escape(datestr))) output.write('

\n') html_mode=0 if prefs['SHOWHR']: output.write('
') output.write('

') if not prefs['SHOWHTML']: output.write('

\n')
    msg.rewindbody()			# Seek to start of message body
    quoted=-1
    while (1):
	L=msg.fp.readline()
	if L=="": break
	if html_mode:
	    # If in HTML mode, check for ending tag; otherwise, we
	    # copy the line without any changes.
	    if nohtmlpat.match(L)==-1:
		output.write(L) ; continue
	    else:
		html_mode=0
		if not prefs['SHOWHTML']: output.write('
\n')
		continue
	# Check for opening  tag
	elif htmlpat.match(L)!=-1:
	    html_mode=1
	    if not prefs['SHOWHTML']: output.write('
\n') continue if prefs['SHOWHTML'] and prefs['IQUOTES']: # Check for a line of quoted text and italicise it # (We have to do this before escaping HTML special # characters because '>' is commonly used.) quoted=quotedpat.match(L) if quoted!=-1: L=cgi.escape(L[:quoted]) + '' + cgi.escape(L[quoted:]) + '' # If we're flowing the message text together, quoted lines # need explicit breaks, no matter what mode we're in. if prefs['SHOWHTML']: L=L+'
' else: L=cgi.escape(L) else: L=cgi.escape(L) # Check for an e-mail address L2="" ; i=emailpat.search(L) while i!=-1: length=len(emailpat.group(1)) mailcmd=prefs['MAILCOMMAND'] % {'TO':L[i:i+length]} L2=L2+'%s%s' % (L[:i], mailcmd, L[i:i+length]) L=L[i+length:] i=emailpat.search(L) L=L2+L ; L2=""; i=urlpat.search(L) while i!=-1: length=len(urlpat.group(1)) L2=L2+'%s%s' % (L[:i], L[i:i+length], L[i:i+length]) L=L[i+length:] i=urlpat.search(L) L=L2+L if prefs['SHOWHTML']: while (L!="" and L[-1] in '\015\012'): L=L[:-1] if prefs['SHOWBR']: # We don't want to flow quoted passages if quoted==-1: L=L+'
' else: # If we're not adding
to each line, we'll need to # insert

markup on blank lines to separate paragraphs. if blankpat.match(L)!=-1: L=L+'

' L=L+'\n' output.write(L) if not prefs['SHOWHTML'] and not html_mode: output.write('

') if prefs['SHOWHR']: output.write('
') output.write('\n\n') output.close() def WriteHTMLIndex(prefs, fp, L, articles, indexname): """Process a list L into an HTML index, written to fp. L is processed from left to right, and contains a list of 2-tuples; an integer of 1 or more giving the depth of indentation, and a list of strings which are used to reference the 'articles' dictionary. Most of the time the lists contain only 1 element.""" fp.write('\n' "" "%s Mailing List Archive by %s\n" % (prefs['LABEL'], indexname)) fp.write('

%s Mailing List Archive by %s

' '

Please inform amk@magnet.com if any of the messages are formatted incorrectly.' % (mailcmd,) ) fp.write("

Starting: %s
" "Ending: %s
Messages: %i

" % (prefs['firstDate'], prefs['endDate'], len(L)) ) # Write the index level=1 fp.write('