167 lines
5.1 KiB
Python
167 lines
5.1 KiB
Python
# General Utility Functions used in our Git scripts
|
|
#
|
|
# Copyright (C) 2008 Owen Taylor
|
|
# Copyright (C) 2009 Red Hat, Inc
|
|
#
|
|
# This program is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU General Public License
|
|
# as published by the Free Software Foundation; either version 2
|
|
# of the License, or (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, If not, see
|
|
# http://www.gnu.org/licenses/.
|
|
|
|
import os
|
|
import sys
|
|
import smtplib
|
|
from subprocess import Popen
|
|
import tempfile
|
|
import time
|
|
import email
|
|
|
|
def die(message):
|
|
print >>sys.stderr, message
|
|
sys.exit(1)
|
|
|
|
# This cleans up our generation code by allowing us to use the same indentation
|
|
# for the first line and subsequent line of a multi-line string
|
|
def strip_string(str):
|
|
start = 0
|
|
end = len(str)
|
|
if len(str) > 0 and str[0] == '\n':
|
|
start += 1
|
|
if len(str) > 1 and str[end - 1] == '\n':
|
|
end -= 1
|
|
|
|
return str[start:end]
|
|
|
|
# How long to wait between mails (in seconds); the idea of waiting
|
|
# is to try to make the sequence of mails we send out in order
|
|
# actually get delivered in order. The waiting is done in a forked
|
|
# subprocess and doesn't stall completion of the main script.
|
|
EMAIL_DELAY = 0.02
|
|
|
|
# Some line that can never appear in any email we send out
|
|
EMAIL_BOUNDARY="---@@@--- labs-git-email ---@@@---\n"
|
|
|
|
def tomail(x):
|
|
if '<' in x:
|
|
x = x[x.index('<')+1:-1]
|
|
return x
|
|
|
|
# Run in subprocess
|
|
def _do_send_emails(email_in):
|
|
email_files = []
|
|
current_file = None
|
|
last_line = None
|
|
|
|
# Read emails from the input pipe and write each to a file
|
|
for line in email_in:
|
|
if current_file is None:
|
|
current_file, filename = tempfile.mkstemp(suffix=".mail", prefix="labs-post-receive-email-")
|
|
email_files.append(filename)
|
|
|
|
if line == EMAIL_BOUNDARY:
|
|
# Strip the last line if blank; see comment when writing
|
|
# the email boundary for rationale
|
|
if last_line.strip() != "":
|
|
os.write(current_file, last_line)
|
|
last_line = None
|
|
os.close(current_file)
|
|
current_file = None
|
|
else:
|
|
if last_line is not None:
|
|
os.write(current_file, last_line)
|
|
last_line = line
|
|
|
|
if current_file is not None:
|
|
if last_line is not None:
|
|
os.write(current_file, last_line)
|
|
os.close(current_file)
|
|
|
|
# We're done interacting with the parent process, the rest happens
|
|
# asynchronously; send out the emails one by one and remove the
|
|
# temporary files
|
|
server = smtplib.SMTP('localhost')
|
|
for i, filename in enumerate(email_files):
|
|
if i != 0:
|
|
time.sleep(EMAIL_DELAY)
|
|
|
|
f = open(filename)
|
|
msgstr = f.read()
|
|
message = email.message_from_string(msgstr)
|
|
fromaddr = tomail(message['From'])
|
|
toaddrs = [tomail(x.strip()) for x in message['To'].split(',')]
|
|
server.sendmail(fromaddr, toaddrs, msgstr)
|
|
|
|
os.remove(filename)
|
|
f.close()
|
|
server.quit()
|
|
|
|
email_file = None
|
|
|
|
# Start a new outgoing email; returns a file object that the
|
|
# email should be written to. Call end_email() when done
|
|
def start_email():
|
|
global email_file
|
|
if email_file is None:
|
|
email_pipe = os.pipe()
|
|
pid = os.fork()
|
|
if pid == 0:
|
|
# The child
|
|
|
|
os.close(email_pipe[1])
|
|
email_in = os.fdopen(email_pipe[0])
|
|
|
|
# Redirect stdin/stdout/stderr to/from /dev/null
|
|
devnullin = os.open("/dev/null", os.O_RDONLY)
|
|
os.close(0)
|
|
os.dup2(devnullin, 0)
|
|
|
|
if False:
|
|
devnullout = os.open("/dev/null", os.O_WRONLY)
|
|
os.close(1)
|
|
os.dup2(devnullout, 1)
|
|
os.close(2)
|
|
os.dup2(devnullout, 2)
|
|
os.close(devnullout)
|
|
|
|
# Fork again to daemonize
|
|
if os.fork() > 0:
|
|
sys.exit(0)
|
|
|
|
try:
|
|
_do_send_emails(email_in)
|
|
except Exception:
|
|
raise
|
|
import syslog
|
|
import traceback
|
|
|
|
syslog.openlog(os.path.basename(sys.argv[0]))
|
|
syslog.syslog(syslog.LOG_ERR, "Unexpected exception sending mail")
|
|
for line in traceback.format_exc().strip().split("\n"):
|
|
syslog.syslog(syslog.LOG_ERR, line)
|
|
|
|
sys.exit(0)
|
|
|
|
email_file = os.fdopen(email_pipe[1], "w")
|
|
else:
|
|
# The email might not end with a newline, so add one. We'll
|
|
# strip the last line, if blank, when emails, so the net effect
|
|
# is to add a newline to messages without one
|
|
email_file.write("\n")
|
|
email_file.write(EMAIL_BOUNDARY)
|
|
|
|
return email_file
|
|
|
|
# Finish an email started with start_email
|
|
def end_email():
|
|
global email_file
|
|
email_file.flush()
|