#!/usr/bin/python # -*- mode: python; coding: utf-8 -*- # Copyright © 2008 Jeffrey C. Ollie # 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 3 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, see # . import os import re import smtplib import socket from qpid.util import connect from qpid.connection import Connection from qpid.datatypes import Message, RangedSet, uuid4 from qpid.queue import Empty from lxml import etree import email.generator import email.message import email.header import email.utils import email.charset from cStringIO import StringIO utf8 = email.charset.Charset('utf-8') utf8.body_encoding = None utf8.header_encoding = email.charset.SHORTEST def get_diff(cvsserver, cvsroot, directory, filename, old_version, new_version): diff = u'' if cvsserver == socket.gethostname(): diff += u'Index: %s\n' % filename diffcmd = '/usr/bin/rcsdiff -kk -u -r%s -r%s "%s,v" 2>&1' % (old_version, new_version, os.path.join(cvsroot, directory, filename)) else: diffcmd = '/usr/bin/cvs -f -d ":pserver:anonymous@%s:%s" diff -kk -u -N -r %s -r %s "%s" 2>/dev/null' % (cvsserver, cvsroot, old_version, new_version, os.path.join(directory, filename)) fp = os.popen(diffcmd) diff += fp.read().decode('utf-8', 'replace') status = fp.close() return diff package_re = re.compile(r'^(?:rpms|devel)/([^/]+)(?:/([^/]+))?') def append_package_owner_email(directory, log_message, recipients): global package_re package_match = package_re.match(directory) if package_match and package_match.group(1): recipients.add('%s-owner@fedoraproject.org' % package_match.group(1)) directory_based_notifications = [(re.compile(r'^rpms'), [append_package_owner_email, 'cvsextras@fedoraproject.org']), (re.compile(r'^devel'), [append_package_owner_email, 'cvsextras@fedoraproject.org']), (re.compile(r'^common'), ['cvsextras@fedoraproject.org', 'jkeating@redhat.com']), (re.compile(r'^owners'), ['cvsextras@fedoraproject.org', 'cvsadmin-menbers@fedoraproject.org']), (re.compile(r'^comps'), ['cvsextras@fedoraproject.org', 'katzj@redhat.com', 'notting@redhat.com', 'jkeating@redhat.com']), (re.compile(r'^example'), ['jeff@ocjtech.us'])] message_based_notifications = [(re.compile(r'docs'), ['relnotes@fedoraproject.org'])] def process_notifications(directory, log_message, recipients, notifications): for notification in notifications: if isinstance(notification, basestring): recipients.add(notification) elif callable(notification): notification(directory, log_message, recipients) def compute_directory_based_notifications(directory, log_message, recipients): for pattern, notifications in directory_based_notifications: match = pattern.match(directory) if match: process_notifications(directory, log_message, recipients, notifications) def compute_message_based_notifications(directory, log_message, email_recipient_set): for pattern, notifications in message_based_notifications: match = pattern.search(log_message) if match: process_notifications(directory, log_message, recipients, notifications) host = '127.0.0.1' port = 5672 sock = connect(host, port) connection = Connection(sock = sock) connection.start() session = connection.session(str(uuid4())) local_queue_name = 'local_queue' local_queue = session.incoming(local_queue_name) session.message_subscribe(queue = 'loginfo_queue', destination = local_queue_name) local_queue.start() message = None while True: try: message = local_queue.get(timeout=10) root_element = etree.fromstring(message.body) cvsserver = root_element.xpath('/loginfo/cvsserver')[0].text cvsroot = root_element.xpath('/loginfo/cvsroot')[0].text directory = root_element.xpath('/loginfo/directory')[0].text username = root_element.xpath('/loginfo/username')[0].text fullname = root_element.xpath('/loginfo/fullname')[0].text timestamp = float(root_element.xpath('/loginfo/timestamp')[0].text) log_message = root_element.xpath('/loginfo/message')[0].text email_sender = email.utils.formataddr((fullname, username + '@fedoraproject.org')) email_body = u'Author: %s\n\n' % email_sender email_body += unicode(log_message) email_body += u'\n\n' email_subject = unicode(directory) for file_element in root_element.xpath('/loginfo/files/file'): filename = file_element.xpath('filename')[0].text old_version = file_element.xpath('old-version')[0].text new_version = file_element.xpath('new-version')[0].text diff = get_diff(cvsserver, cvsroot, directory, filename, old_version, new_version) diff_element = etree.SubElement(file_element, u'diff') diff_element.text = diff email_body += diff email_body += u'\n\n' email_subject += u' %s,%s,%s' % (filename, old_version, new_version) # build the list of recipients recipients = set() compute_directory_based_notifications(directory, log_message, recipients) compute_message_based_notifications(directory, log_message, recipients) if len(recipients) == 0: recipients.add('admin@fedoraproject.org') email_recipients = list(recipients) email_recipients.sort() # Build the email object msg = email.message.Message() msg.set_charset(utf8) msg.set_payload(email_body.encode('utf-8')) msg['From'] = email.header.Header(unicode(email_sender), utf8) msg['To'] = email.header.Header(u', '.join(email_recipients), utf8) msg['Subject'] = email.header.Header(email_subject, utf8) msg['Date'] = email.utils.formatdate(timestamp) msg['X-CVSROOT'] = cvsroot msg['X-CVS-Module'] = directory.split('/', 1)[0] msg['X-CVS-Directory'] = directory msg['X-CVS-User'] = username msg['X-CVS-Server'] = cvsserver msg['Precedence'] = 'first-class' # and send it out msg_fp = StringIO() msg_g = email.generator.Generator(msg_fp, False) msg_g.flatten(msg) s = smtplib.SMTP() s.connect() s.sendmail(email_sender, email_recipients, msg_fp.getvalue()) s.close() session.message_accept(RangedSet(message.id)) except Empty: print 'No more messages!' break session.close(timeout=10)