[cairo-commit] CVSROOT syncmail,1.4,1.5
Carl Worth
cworth at pdx.freedesktop.org
Wed Oct 15 12:53:56 PDT 2003
- Previous message: [cairo-commit] cairo BUGS,1.5,1.6
- Next message: [cairo-commit] cairo/doc/reference crossreference.rb,NONE,1.1 ChangeLog,1.1,1.2 README,1.1,1.2 doc.xml,1.1,1.2
- Messages sorted by:
[ date ]
[ thread ]
[ subject ]
[ author ]
Update of /cvs/cairo/CVSROOT
In directory pdx:/tmp/cvs-serv2463
Modified Files:
syncmail
Log Message:
Switched to a newer(?) version of syncmail snarfed from keithp.com.
This one sets an explicit From address so we can make it static to get past mailman.
Index: syncmail
===================================================================
RCS file: /cvs/cairo/CVSROOT/syncmail,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -d -r1.4 -r1.5
--- syncmail 25 Sep 2003 18:31:14 -0000 1.4
+++ syncmail 15 Oct 2003 19:53:54 -0000 1.5
@@ -1,6 +1,12 @@
#! /usr/bin/python
-# -*- Python -*-
-
+
+# Copyright (c) 2002, Barry Warsaw, Fred Drake, and contributors
+# All rights reserved.
+# See the accompanying LICENSE file for details.
+
+# NOTE: Until SourceForge installs a modern version of Python on the cvs
+# servers, this script MUST be compatible with Python 1.5.2.
+
"""Complicated notification for CVS checkins.
This script is used to provide email notifications of changes to the CVS
@@ -29,15 +35,11 @@
%(PROGRAM)s [options] <%%S> email-addr [email-addr ...]
-Where options is:
+Where options are:
--cvsroot=<path>
- Use <path> as the environment variable CVSROOT. Otherwise this
- variable must exist in the environment.
-
- --help
- -h
- Print this text.
+ Use <path> as the environment variable CVSROOT. Otherwise this
+ variable must exist in the environment.
--context=#
-C #
@@ -46,8 +48,35 @@
-c
Produce a context diff (default).
+ -m hostname
+ --mailhost hostname
+ The hostname of an available SMTP server. The default is
+ 'localhost'.
+
-u
- Produce a unified diff (smaller, but harder to read).
+ Produce a unified diff (smaller).
+
+ -S TEXT
+ --subject-prefix=TEXT
+ Prepend TEXT to the email subject line.
+
+ -R ADDR
+ --reply-to=ADDR
+ Add a "Reply-To: ADDR" header to the email message.
+
+ --quiet / -q
+ Don't print as much status to stdout.
+
+ --fromhost=hostname
+ -f hostname
+ The hostname that email messages appear to be coming from. The From:
+ header of the outgoing message will look like user at hostname. By
+ default, hostname is the machine's fully qualified domain name.
+
+ --help / -h
+ Print this text.
+
+The rest of the command line arguments are:
<%%S>
CVS %%s loginfo expansion. When invoked by CVS, this will be a single
@@ -58,25 +87,63 @@
email-addrs
At least one email address.
-
"""
+__version__ = '1.2'
import os
import sys
-import string
+import re
import time
+import string
import getopt
+import smtplib
+import pwd
+import socket
-# Notification command
-MAILCMD = '/usr/bin/mail -s "%(SUBJECT)s" %(PEOPLE)s 2>&1 > /dev/null'
+try:
+ from socket import getfqdn
+except ImportError:
+ def getfqdn():
+ # Python 1.5.2 :(
+ hostname = socket.gethostname()
+ byaddr = socket.gethostbyaddr(socket.gethostbyname(hostname))
+ aliases = byaddr[1]
+ aliases.insert(0, byaddr[0])
+ aliases.insert(0, hostname)
+ for fqdn in aliases:
+ if '.' in fqdn:
+ break
+ else:
+ fqdn = 'localhost.localdomain'
+ return fqdn
+
+
+from cStringIO import StringIO
+
+# Which SMTP server to do we connect to?
+MAILHOST = 'localhost'
+MAILPORT = 25
# Diff trimming stuff
DIFF_HEAD_LINES = 20
DIFF_TAIL_LINES = 20
DIFF_TRUNCATE_IF_LARGER = 1000
+EMPTYSTRING = ''
+SPACE = ' '
+DOT = '.'
+COMMASPACE = ', '
+
PROGRAM = sys.argv[0]
+BINARY_EXPLANATION_LINES = [
+ "(This appears to be a binary file; contents omitted.)\n"
+ ]
+
+REVCRE = re.compile("^(NONE|[0-9.]+)$")
+NOVERSION = "Couldn't generate diff; no version number found in filespec: %s"
+BACKSLASH = "Couldn't generate diff: backslash in filespec's filename: %s"
+
def usage(code, msg=''):
@@ -88,20 +155,60 @@
def calculate_diff(filespec, contextlines):
- try:
- file, oldrev, newrev = string.split(filespec, ',')
- except ValueError:
- # No diff to report
- return '***** Bogus filespec: %s' % filespec
+ spec = string.split(filespec, ',')
+ if len(spec) < 3:
+ # Too few parts; command line probable used a replacement
+ # other than "%{sVv}"; don't fail, but don't produce a diff
+ # since we can't be sure what diff to generate.
+ return ''
+ # This allows filenames that contain commas:
+ file = string.join(spec[:-2], ",")
+ oldrev = spec[-2]
+ newrev = spec[-1]
+
+ # Make sure we can find a CVS version number
+ if not REVCRE.match(oldrev):
+ return NOVERSION % filespec
+ if not REVCRE.match(newrev):
+ return NOVERSION % filespec
+
+ if string.find(file, '\\') <> -1:
+ # I'm sorry, a file name that contains a backslash is just too much.
+ # XXX if someone wants to figure out how to escape the backslashes in
+ # a safe way to allow filenames containing backslashes, this is the
+ # place to do it. --Zooko 2002-03-17
+ return BACKSLASH % filespec
+
+ if string.find(file, "'") <> -1:
+ # Those crazy users put single-quotes in their file names! Now we
+ # have to escape everything that is meaningful inside double-quotes.
+ filestr = string.replace(file, '`', '\`')
+ filestr = string.replace(filestr, '"', '\"')
+ filestr = string.replace(filestr, '$', '\$')
+ # and quote it with double-quotes.
+ filestr = '"' + filestr + '"'
+ else:
+ # quote it with single-quotes.
+ filestr = "'" + file + "'"
if oldrev == 'NONE':
+ # File is being added.
try:
if os.path.exists(file):
fp = open(file)
else:
- update_cmd = 'cvs -fn update -r %s -p %s' % (newrev, file)
+ update_cmd = "cvs -fn update -r %s -p %s" % (newrev, filestr)
fp = os.popen(update_cmd)
lines = fp.readlines()
fp.close()
+ # Is this a binary file? Let's look at the first few
+ # lines to figure it out:
+ for line in lines[:5]:
+ for c in string.rstrip(line):
+ if c in string.whitespace:
+ continue
+ if c < ' ' or c > chr(127):
+ lines = BINARY_EXPLANATION_LINES[:]
+ break
lines.insert(0, '--- NEW FILE: %s ---\n' % file)
except IOError, e:
lines = ['***** Error reading new file: ',
@@ -109,59 +216,106 @@
elif newrev == 'NONE':
lines = ['--- %s DELETED ---\n' % file]
else:
+ # File has been changed.
# This /has/ to happen in the background, otherwise we'll run into CVS
# lock contention. What a crock.
if contextlines > 0:
difftype = "-C " + str(contextlines)
else:
difftype = "-u"
- diffcmd = "/usr/bin/cvs -f diff -kk %s --minimal -r %s -r %s '%s'" % (
- difftype, oldrev, newrev, file)
+ diffcmd = "/usr/bin/cvs -f diff -kk %s --minimal -r %s -r %s %s" \
+ % (difftype, oldrev, newrev, filestr)
fp = os.popen(diffcmd)
lines = fp.readlines()
sts = fp.close()
# ignore the error code, it always seems to be 1 :(
## if sts:
## return 'Error code %d occurred during diff\n' % (sts >> 8)
-# if len(lines) > DIFF_TRUNCATE_IF_LARGER:
-# removedlines = len(lines) - DIFF_HEAD_LINES - DIFF_TAIL_LINES
-# del lines[DIFF_HEAD_LINES:-DIFF_TAIL_LINES]
-# lines.insert(DIFF_HEAD_LINES,
-# '[...%d lines suppressed...]\n' % removedlines)
+ if len(lines) > DIFF_TRUNCATE_IF_LARGER:
+ removedlines = len(lines) - DIFF_HEAD_LINES - DIFF_TAIL_LINES
+ del lines[DIFF_HEAD_LINES:-DIFF_TAIL_LINES]
+ lines.insert(DIFF_HEAD_LINES,
+ '[...%d lines suppressed...]\n' % removedlines)
return string.join(lines, '')
-def blast_mail(mailcmd, filestodiff, contextlines):
+rfc822_specials_re = re.compile(r'[\(\)\<\>\@\,\;\:\\\"\.\[\]]')
+
+def quotename(name):
+ if name and rfc822_specials_re.search(name):
+ return '"%s"' % string.replace(name, '"', '\\"')
+ else:
+ return name
+
+
+
+def blast_mail(subject, people, filestodiff, contextlines, fromhost, replyto):
# cannot wait for child process or that will cause parent to retain cvs
# lock for too long. Urg!
if not os.fork():
# in the child
# give up the lock you cvs thang!
time.sleep(2)
- fp = os.popen(mailcmd, 'w')
- fp.write(sys.stdin.read())
- fp.write('\n')
- # append the diffs if available
- for file in filestodiff:
- fp.write(calculate_diff(file, contextlines))
- fp.write('\n')
- fp.close()
- # doesn't matter what code we return, it isn't waited on
+ # Create the smtp connection to the localhost
+ conn = smtplib.SMTP()
+ conn.connect(MAILHOST, MAILPORT)
+ user = pwd.getpwuid(os.getuid())[0]
+ name = string.split(pwd.getpwuid(os.getuid())[4], ',')[0]
+ domain = fromhost or getfqdn()
+ address = '%s@%s' % (user, domain)
+ s = StringIO()
+ sys.stdout = s
+ try:
+ vars = {'name' : quotename(name),
+ 'people' : string.join(people, COMMASPACE),
+ 'subject' : subject,
+ 'version' : __version__,
+ 'user' : user,
+ }
+ print '''\
+From: %(name)s <commit at pdx.freedesktop.org>
+To: %(people)s''' % vars
+ if replyto:
+ print 'Reply-To: %s' % replyto
+ print '''\
+Subject: %(subject)s
+X-Mailer: Python syncmail %(version)s <http://sf.net/projects/cvs-syncmail>
+
+Committed by: %(user)s
+''' % vars
+ s.write(sys.stdin.read())
+ # append the diffs if available
+ print
+ for file in filestodiff:
+ print calculate_diff(file, contextlines)
+ finally:
+ sys.stdout = sys.__stdout__
+ resp = conn.sendmail(address, people, s.getvalue())
+ conn.close()
os._exit(0)
# scan args for options
def main():
- contextlines = 0
+ # XXX Should really move all the options to an object, just to
+ # avoid threading so many positional args through everything.
try:
- opts, args = getopt.getopt(sys.argv[1:], 'hC:cu',
- ['context=', 'cvsroot=', 'help'])
+ opts, args = getopt.getopt(
+ sys.argv[1:], 'hC:cuS:R:qf:m:',
+ ['fromhost=', 'context=', 'cvsroot=', 'mailhost=',
+ 'subject-prefix=', 'reply-to=',
+ 'help', 'quiet'])
except getopt.error, msg:
usage(1, msg)
# parse the options
+ contextlines = 2
+ verbose = 1
+ subject_prefix = ""
+ replyto = None
+ fromhost = None
for opt, arg in opts:
if opt in ('-h', '--help'):
usage(0)
@@ -174,6 +328,17 @@
contextlines = 2
elif opt == '-u':
contextlines = 0
+ elif opt in ('-S', '--subject-prefix'):
+ subject_prefix = arg
+ elif opt in ('-R', '--reply-to'):
+ replyto = arg
+ elif opt in ('-q', '--quiet'):
+ verbose = 0
+ elif opt in ('-f', '--fromhost'):
+ fromhost = arg
+ elif opt in ('-m', '--mailhost'):
+ global MAILHOST
+ MAILHOST = arg
# What follows is the specification containing the files that were
# modified. The argument actually must be split, with the first component
@@ -181,7 +346,7 @@
# $CVSROOT, followed by the list of files that are changing.
if not args:
usage(1, 'No CVS module specified')
- SUBJECT = args[0]
+ subject = subject_prefix + args[0]
specs = string.split(args[0])
del args[0]
@@ -190,26 +355,30 @@
usage(1, 'No recipients specified')
# Now do the mail command
- PEOPLE = string.join(args)
- mailcmd = MAILCMD % vars()
+ people = args
- print 'Mailing %s...' % PEOPLE
- if specs == ['-', 'Imported', 'sources']:
- return
- if specs[-3:] == ['-', 'New', 'directory']:
+ if verbose:
+ print 'Mailing %s...' % string.join(people, COMMASPACE)
+
+ if specs[-3:] == ['-', 'Imported', 'sources']:
+ del specs[-3:]
+ elif specs[-3:] == ['-', 'New', 'directory']:
del specs[-3:]
elif len(specs) > 2:
L = specs[:2]
for s in specs[2:]:
prev = L[-1]
- if string.count(prev, ",") < 2:
+ if string.count(prev, ',') < 2:
L[-1] = "%s %s" % (prev, s)
else:
L.append(s)
specs = L
- print 'Generating notification message...'
- blast_mail(mailcmd, specs[1:], contextlines)
- print 'Generating notification message... done.'
+
+ if verbose:
+ print 'Generating notification message...'
+ blast_mail(subject, people, specs[1:], contextlines, fromhost, replyto)
+ if verbose:
+ print 'Generating notification message... done.'
- Previous message: [cairo-commit] cairo BUGS,1.5,1.6
- Next message: [cairo-commit] cairo/doc/reference crossreference.rb,NONE,1.1 ChangeLog,1.1,1.2 README,1.1,1.2 doc.xml,1.1,1.2
- Messages sorted by:
[ date ]
[ thread ]
[ subject ]
[ author ]
More information about the cairo-commit
mailing list