amoralej / centos / cbs-tools

Forked from centos/cbs-tools 2 years ago
Blob Blame Raw
# CGI script to handle file updates for the rpms CVS repository. There
# is nothing really complex here other than tedious checking of our
# every step along the way...
# Written for Fedora, modified to suit CentOS Infrastructure.
# Modified by Howard Johnson <> 2014
# License: GPL

# centos' lookaside is a bit differently laid out to fedora's.
# centos uses a <package>/<branch>/<sha1sum> scheme.
# The upload.cgi gets called with the following arguments:
#   name - package (git repo) name
#   branch - branch name
#   sha1sum - SHA1 checksum of the file
#   file - the file to upload (optional)
# With only the first three args, the script runs in check mode.
# With the fourth too, it operates in upload mode.

import os
import sys
import cgi
import tempfile
import syslog
import smtplib
import re
from ConfigParser import SafeConfigParser

from email import Header, Utils
    from email.mime.text import MIMEText
except ImportError:
    from email.MIMEText import MIMEText

import hashlib
sha1_constructor = hashlib.sha1

# Config file with all our settings
CONFIG = '/etc/lookaside.cfg'
conf = SafeConfigParser()

# Reading buffer size

# Gitblit config file regexes
SECTION = re.compile(r'\[(.+)\]')
OPTION = re.compile(r'(.+)=(.+)')
REPO = re.compile(r'([^/]+).git$')

def merge_gitblit_section(repoacl, repos, users):
    for repo in repos:
        if repo not in repoacl:
            repoacl[repo] = []
        for user in users:
            if user not in repoacl[repo]:

# turns the gitblit config file into a dict of package name to permitted users
def parse_gitblit_config(filename):
    insection = False
    repoacl = {}
    sectrepos = []
    sectusers = []

    f = open(filename)
    for line in f:
        if line.strip() == '' or line[0] == '#':
        secth = SECTION.match(line)
        if secth:
            if insection:
                merge_gitblit_section(repoacl, sectrepos, sectusers)

                sectrepos = []
                sectusers = []
                insection = True
        opt = OPTION.match(line)
        if opt:
            if not insection:
            key =
            value =

            if key == "repository":
                pack =
                if pack:
            elif key == "user":
    if insection:
        merge_gitblit_section(repoacl, sectrepos, sectusers)
    return repoacl

def send_error(text):
    print text

def check_form(form, var):
    ret = form.getvalue(var, None)
    if ret is None:
        send_error('Required field "%s" is not present.' % var)
    if isinstance(ret, list):
        send_error('Multiple values given for "%s". Aborting.' % var)
    return ret

def send_email(pkg, sha1, filename, username):
    text = """A file has been added to the lookaside cache for %(pkg)s:

%(sha1)s  %(filename)s""" % locals()
    msg = MIMEText(text)
    sender_name = conf.get('mail', 'sender_name')
    sender_email = conf.get('mail', 'sender_email')
    sender = Utils.formataddr((sender_name, sender_email))
    recipient = conf.get('mail', 'recipient')
    msg['Subject'] = 'File %s uploaded to lookaside cache by %s' % (
            filename, username)
    msg['From'] = sender
    msg['To'] = recipient
        s = smtplib.SMTP(conf.get('mail', 'smtp_server'))
        s.sendmail(sender, recipient, msg.as_string())
        errstr = 'sending mail for upload of %s failed!' % filename
        print >> sys.stderr, errstr

def main():

    username = os.environ.get('SSL_CLIENT_S_DN_CN', None)

    print 'Content-Type: text/plain'

    assert os.environ['REQUEST_URI'].split('/')[1] == 'lookaside'

    form = cgi.FieldStorage()
    name = check_form(form, 'name')
    branch = check_form(form, 'branch')
    sha1sum = check_form(form, 'sha1sum')

    action = None
    upload_file = None
    filename = None

    # Is this a submission or a test?
    # in a test, we don't get a file.
    if not form.has_key('file'):
        action = 'check'
        print >> sys.stderr, '[username=%s] Checking file status: NAME=%s BRANCH=%s SHA1SUM=%s' % (username, name, branch, sha1sum)
        action = 'upload'
        upload_file = form['file']
        if not upload_file.file:
            send_error('No file given for upload. Aborting.')
        filename = os.path.basename(upload_file.filename)
        print >> sys.stderr, '[username=%s] Processing upload request: NAME=%s BRANCH=%s SHA1SUM=%s' % (username, name, branch, sha1sum)

    module_dir = os.path.join(conf.get('lookaside', 'cache_dir'), name, branch)
    dest_file =  os.path.join(module_dir, sha1sum)

    # try to see if we already have this file...
    if os.path.exists(dest_file):
        if action == 'check':
            print 'Available'
            dest_file_stat = os.stat(dest_file)
            print 'File %s already exists' % filename
            print 'File: %s Size: %d' % (dest_file, dest_file_stat.st_size)
    elif action == 'check':
        print 'Missing'

    # if desired, make sure the user has permission to write to this branch
    if conf.getboolean('acl', 'do_acl'):
        # load in the gitblit config
        repoacl = parse_gitblit_config(conf.get('acl', 'gitblit_config'))

        # if the package isn't in the gitblit config, we can't give upload perms
        if name not in repoacl:
            send_error("Unknown package %s" % name)

        # now check the perms
        if username not in repoacl[name]:
            send_error("Write access package %s rejected for user %s" % (name, username))

    # check that all directories are in place
    if not os.path.isdir(module_dir):
        os.makedirs(module_dir, 02775)

    # grab a temporary filename and dump our file in there
    tempfile.tempdir = module_dir
    tmpfile = tempfile.mkstemp(sha1sum)[1]
    tmpfd = open(tmpfile, 'w')

    # now read the whole file in
    m = sha1_constructor()
    filesize = 0
    while True:
        data =
        if not data:
        filesize += len(data)

    # now we're done reading, check the MD5 sum of what we got
    check_sha1sum = m.hexdigest()
    if sha1sum != check_sha1sum:
        send_error("SHA1 check failed. Received %s instead of %s." % (check_sha1sum, sha1sum))

    # rename it its final name
    os.rename(tmpfile, dest_file)
    os.chmod(dest_file, 0644)

    print >> sys.stderr, '[username=%s] Stored %s (%d bytes)' % (username, dest_file, filesize)
    print 'File %s size %d SHA1 %s stored OK' % (filename, filesize, sha1sum)
    if conf.getboolean('mail', 'send_mail'):
        send_email(name, sha1sum, filename, username)

if __name__ == '__main__':