Пример #1
0
def _addlist(mlist, fp):
    # Set up the mailman-loop address
    loopaddr = Utils.ParseEmail(Utils.get_site_email(extra='loop'))[0]
    loopmbox = os.path.join(mm_cfg.DATA_DIR, 'owner-bounces.mbox')
    # Seek to the end of the text file, but if it's empty write the standard
    # disclaimer, and the loop catch address.
    fp.seek(0, 2)
    if not fp.tell():
        print >> fp, """\
# This file is generated by Mailman, and is kept in sync with the
# binary hash file aliases.db.  YOU SHOULD NOT MANUALLY EDIT THIS FILE
# unless you know what you're doing, and can keep the two files properly
# in sync.  If you screw it up, you're on your own.
"""
        print >> fp, '# The ultimate loop stopper address'
        print >> fp, '%s: %s' % (loopaddr, loopmbox)
        print >> fp
    # Bootstrapping.  bin/genaliases must be run before any lists are created,
    # but if no lists exist yet then mlist is None.  The whole point of the
    # exercise is to get the minimal aliases.db file into existance.
    if mlist is None:
        return
    listname = mlist.internal_name()
    fieldsz = len(listname) + len('-unsubscribe')
    # The text file entries get a little extra info
    print >> fp, '# STANZA START:', listname
    print >> fp, '# CREATED:', time.ctime(time.time())
    # Now add all the standard alias entries
    for k, v in makealiases(listname):
        # Format the text file nicely
        print >> fp, k + ':', ((fieldsz - len(k)) * ' ') + v
    # Finish the text file stanza
    print >> fp, '# STANZA END:', listname
    print >> fp
Пример #2
0
def verp_probe(mlist, msg):
    bmailbox, bdomain = Utils.ParseEmail(mlist.GetBouncesEmail())
    # Sadly not every MTA bounces VERP messages correctly, or consistently.
    # Fall back to Delivered-To: (Postfix), Envelope-To: (Exim) and
    # Apparently-To:, and then short-circuit if we still don't have anything
    # to work with.  Note that there can be multiple Delivered-To: headers so
    # we need to search them all (and we don't worry about false positives for
    # forwarded email, because only one should match VERP_REGEXP).
    vals = []
    for header in ('to', 'delivered-to', 'envelope-to', 'apparently-to'):
        vals.extend(msg.get_all(header, []))
    for field in vals:
        to = parseaddr(field)[1]
        if not to:
            continue  # empty header
        mo = re.search(mm_cfg.VERP_PROBE_REGEXP, to)
        if not mo:
            continue  # no match of regexp
        try:
            if bmailbox <> mo.group('bounces'):
                continue  # not a bounce to our list
            # Extract the token and see if there's an entry
            token = mo.group('token')
            data = mlist.pend_confirm(token, expunge=False)
            if data is not None:
                return token
        except IndexError:
            syslog(
                'error',
                "VERP_PROBE_REGEXP doesn't yield the right match groups: %s",
                mm_cfg.VERP_PROBE_REGEXP)
    return None
Пример #3
0
def _addvirtual(mlist, fp):
    listname = mlist.internal_name()
    fieldsz = len(listname) + len('-unsubscribe')
    hostname = mlist.host_name
    # Set up the mailman-loop address
    loopaddr = Utils.get_site_email(mlist.host_name, extra='loop')
    loopdest = Utils.ParseEmail(loopaddr)[0]
    if mm_cfg.VIRTUAL_MAILMAN_LOCAL_DOMAIN:
        loopdest += '@' + mm_cfg.VIRTUAL_MAILMAN_LOCAL_DOMAIN
    # Seek to the end of the text file, but if it's empty write the standard
    # disclaimer, and the loop catch address.
    fp.seek(0, 2)
    if not fp.tell():
        print >> fp, """\
# This file is generated by Mailman, and is kept in sync with the binary hash
# file virtual-mailman.db.  YOU SHOULD NOT MANUALLY EDIT THIS FILE unless you
# know what you're doing, and can keep the two files properly in sync.  If you
# screw it up, you're on your own.
#
# Note that you should already have this virtual domain set up properly in
# your Postfix installation.  See README.POSTFIX for details.

# LOOP ADDRESSES START
%s\t%s
# LOOP ADDRESSES END
""" % (loopaddr, loopdest)
    # The text file entries get a little extra info
    print >> fp, '# STANZA START:', listname
    print >> fp, '# CREATED:', time.ctime(time.time())
    # Now add all the standard alias entries
    for k, v in makealiases(listname):
        fqdnaddr = '%s@%s' % (k, hostname)
        if mm_cfg.VIRTUAL_MAILMAN_LOCAL_DOMAIN:
            localaddr = '%s@%s' % (k, mm_cfg.VIRTUAL_MAILMAN_LOCAL_DOMAIN)
        else:
            localaddr = k
        # Format the text file nicely
        print >> fp, fqdnaddr, ((fieldsz - len(k)) * ' '), localaddr
    # Finish the text file stanza
    print >> fp, '# STANZA END:', listname
    print >> fp
Пример #4
0
def _check_for_virtual_loopaddr(mlist, filename):
    loopaddr = Utils.get_site_email(mlist.host_name, extra='loop')
    loopdest = Utils.ParseEmail(loopaddr)[0]
    infp = open(filename)
    omask = os.umask(007)
    try:
        outfp = open(filename + '.tmp', 'w')
    finally:
        os.umask(omask)
    try:
        # Find the start of the loop address block
        while True:
            line = infp.readline()
            if not line:
                break
            outfp.write(line)
            if line.startswith('# LOOP ADDRESSES START'):
                break
        # Now see if our domain has already been written
        while True:
            line = infp.readline()
            if not line:
                break
            if line.startswith('# LOOP ADDRESSES END'):
                # It hasn't
                print >> outfp, '%s\t%s' % (loopaddr, loopdest)
                outfp.write(line)
                break
            elif line.startswith(loopaddr):
                # We just found it
                outfp.write(line)
                break
            else:
                # This isn't our loop address, so spit it out and continue
                outfp.write(line)
        outfp.writelines(infp.readlines())
    finally:
        infp.close()
        outfp.close()
    os.rename(filename + '.tmp', filename)
Пример #5
0
def verpdeliver(mlist, msg, msgdata, envsender, failures, conn):
    for recip in msgdata['recips']:
        # We now need to stitch together the message with its header and
        # footer.  If we're VERPIng, we have to calculate the envelope sender
        # for each recipient.  Note that the list of recipients must be of
        # length 1.
        #
        # BAW: ezmlm includes the message number in the envelope, used when
        # sending a notification to the user telling her how many messages
        # they missed due to bouncing.  Neat idea.
        msgdata['recips'] = [recip]
        # Make a copy of the message and decorate + delivery that
        msgcopy = copy.deepcopy(msg)
        Decorate.process(mlist, msgcopy, msgdata)
        # Calculate the envelope sender, which we may be VERPing
        if msgdata.get('verp'):
            bmailbox, bdomain = Utils.ParseEmail(envsender)
            rmailbox, rdomain = Utils.ParseEmail(recip)
            if rdomain is None:
                # The recipient address is not fully-qualified.  We can't
                # deliver it to this person, nor can we craft a valid verp
                # header.  I don't think there's much we can do except ignore
                # this recipient.
                syslog('smtp', 'Skipping VERP delivery to unqual recip: %s',
                       recip)
                continue
            d = {'bounces': bmailbox,
                 'mailbox': rmailbox,
                 'host'   : DOT.join(rdomain),
                 }
            envsender = '%s@%s' % ((mm_cfg.VERP_FORMAT % d), DOT.join(bdomain))
        if mlist.personalize == 2:
            # When fully personalizing, we want the To address to point to the
            # recipient, not to the mailing list
            del msgcopy['to']
            name = None
            if mlist.isMember(recip):
                name = mlist.getMemberName(recip)
            if name:
                # Convert the name to an email-safe representation.  If the
                # name is a byte string, convert it first to Unicode, given
                # the character set of the member's language, replacing bad
                # characters for which we can do nothing about.  Once we have
                # the name as Unicode, we can create a Header instance for it
                # so that it's properly encoded for email transport.
                charset = Utils.GetCharSet(mlist.getMemberLanguage(recip))
                if charset == 'us-ascii':
                    # Since Header already tries both us-ascii and utf-8,
                    # let's add something a bit more useful.
                    charset = 'iso-8859-1'
                charset = Charset(charset)
                codec = charset.input_codec or 'ascii'
                if not isinstance(name, str):
                    name = str(name, codec, 'replace')
                name = Header(name, charset).encode()
                msgcopy['To'] = formataddr((name, recip))
            else:
                msgcopy['To'] = recip
        # We can flag the mail as a duplicate for each member, if they've
        # already received this message, as calculated by Message-ID.  See
        # AvoidDuplicates.py for details.
        del msgcopy['x-mailman-copy']
        if recip in msgdata.get('add-dup-header', {}):
            msgcopy['X-Mailman-Copy'] = 'yes'
        # If desired, add the RCPT_BASE64_HEADER_NAME header
        if len(mm_cfg.RCPT_BASE64_HEADER_NAME) > 0:
            del msgcopy[mm_cfg.RCPT_BASE64_HEADER_NAME]
            msgcopy[mm_cfg.RCPT_BASE64_HEADER_NAME] = b64encode(recip)
        # For the final delivery stage, we can just bulk deliver to a party of
        # one. ;)
        bulkdeliver(mlist, msgcopy, msgdata, envsender, failures, conn)
Пример #6
0
def _check_for_virtual_loopaddr(mlist, filename):
    loopaddr = Utils.get_site_email(mlist.host_name, extra='loop')
    loopdest = Utils.ParseEmail(loopaddr)[0]
    siteaddr = Utils.get_site_email(mlist.host_name)
    sitedest = Utils.ParseEmail(siteaddr)[0]
    siteowneraddr = Utils.get_site_email(mlist.host_name, extra='owner')
    siteownerdest = Utils.ParseEmail(siteowneraddr)[0]
    if mm_cfg.VIRTUAL_MAILMAN_LOCAL_DOMAIN:
        loopdest += '@' + mm_cfg.VIRTUAL_MAILMAN_LOCAL_DOMAIN
        sitedest += '@' + mm_cfg.VIRTUAL_MAILMAN_LOCAL_DOMAIN
        siteownerdest += '@' + mm_cfg.VIRTUAL_MAILMAN_LOCAL_DOMAIN
    # If the site list's host_name is a virtual domain, adding the list and
    # owner addresses to the SITE ADDRESSES will duplicate the entries in the
    # stanza for the list.  Postfix doesn't like dups so we try to comment them
    # here, but only for the actual site list domain.
    if (MailList(mm_cfg.MAILMAN_SITE_LIST,
                 lock=False).host_name.lower() == mlist.host_name.lower()):
        siteaddr = '#' + siteaddr
        siteowneraddr = '#' + siteowneraddr
    infp = open(filename)
    omask = os.umask(007)
    try:
        outfp = open(filename + '.tmp', 'w')
    finally:
        os.umask(omask)
    try:
        # Find the start of the loop address block
        while True:
            line = infp.readline()
            if not line:
                break
            outfp.write(line)
            if line.startswith('# LOOP ADDRESSES START'):
                break
        # Now see if our domain has already been written
        while True:
            line = infp.readline()
            if not line:
                break
            if line.startswith('# LOOP ADDRESSES END'):
                # It hasn't
                print >> outfp, '%s\t%s' % (loopaddr, loopdest)
                outfp.write(line)
                break
            elif line.startswith(loopaddr):
                # We just found it
                outfp.write(line)
                break
            else:
                # This isn't our loop address, so spit it out and continue
                outfp.write(line)
        # Now do it all again for the site list address. It must follow the
        # loop addresses.
        while True:
            line = infp.readline()
            if not line:
                break
            outfp.write(line)
            if line.startswith('# SITE ADDRESSES START'):
                break
        # Now see if our domain has already been written
        while True:
            line = infp.readline()
            if not line:
                break
            if line.startswith('# SITE ADDRESSES END'):
                # It hasn't
                print >> outfp, '%s\t%s' % (siteaddr, sitedest)
                print >> outfp, '%s\t%s' % (siteowneraddr, siteownerdest)
                outfp.write(line)
                break
            elif line.startswith(siteaddr) or line.startswith('#' + siteaddr):
                # We just found it
                outfp.write(line)
                break
            else:
                # This isn't our loop address, so spit it out and continue
                outfp.write(line)
        outfp.writelines(infp.readlines())
    finally:
        infp.close()
        outfp.close()
    os.rename(filename + '.tmp', filename)
Пример #7
0
def _addvirtual(mlist, fp):
    listname = mlist.internal_name()
    fieldsz = len(listname) + len('-unsubscribe')
    hostname = mlist.host_name
    # Set up the mailman-loop address
    loopaddr = Utils.get_site_email(mlist.host_name, extra='loop')
    loopdest = Utils.ParseEmail(loopaddr)[0]
    # And the site list posting address.
    siteaddr = Utils.get_site_email(mlist.host_name)
    sitedest = Utils.ParseEmail(siteaddr)[0]
    # And the site list -owner address.
    siteowneraddr = Utils.get_site_email(mlist.host_name, extra='owner')
    siteownerdest = Utils.ParseEmail(siteowneraddr)[0]
    if mm_cfg.VIRTUAL_MAILMAN_LOCAL_DOMAIN:
        loopdest += '@' + mm_cfg.VIRTUAL_MAILMAN_LOCAL_DOMAIN
        sitedest += '@' + mm_cfg.VIRTUAL_MAILMAN_LOCAL_DOMAIN
        siteownerdest += '@' + mm_cfg.VIRTUAL_MAILMAN_LOCAL_DOMAIN
    # If the site list's host_name is a virtual domain, adding the list and
    # owner addresses to the SITE ADDRESSES will duplicate the entries in the
    # stanza for the list.  Postfix doesn't like dups so we try to comment them
    # here, but only for the actual site list domain.
    if (MailList(mm_cfg.MAILMAN_SITE_LIST,
                 lock=False).host_name.lower() == hostname.lower()):
        siteaddr = '#' + siteaddr
        siteowneraddr = '#' + siteowneraddr
    # Seek to the end of the text file, but if it's empty write the standard
    # disclaimer, and the loop catch address and site address.
    fp.seek(0, 2)
    if not fp.tell():
        print >> fp, """\
# This file is generated by Mailman, and is kept in sync with the binary hash
# file virtual-mailman.db.  YOU SHOULD NOT MANUALLY EDIT THIS FILE unless you
# know what you're doing, and can keep the two files properly in sync.  If you
# screw it up, you're on your own.
#
# Note that you should already have this virtual domain set up properly in
# your Postfix installation.  See README.POSTFIX for details.

# LOOP ADDRESSES START
%s\t%s
# LOOP ADDRESSES END

# We also add the site list address in each virtual domain as that address
# is exposed on admin and listinfo overviews, and we add the site list-owner
# address as it is exposed in the list created email notice.

# SITE ADDRESSES START
%s\t%s
%s\t%s
# SITE ADDRESSES END
""" % (loopaddr, loopdest, siteaddr, sitedest, siteowneraddr, siteownerdest)
    # The text file entries get a little extra info
    print >> fp, '# STANZA START:', listname
    print >> fp, '# CREATED:', time.ctime(time.time())
    # Now add all the standard alias entries
    for k, v in makealiases(listname):
        fqdnaddr = '%s@%s' % (k, hostname)
        if mm_cfg.VIRTUAL_MAILMAN_LOCAL_DOMAIN:
            localaddr = '%s@%s' % (k, mm_cfg.VIRTUAL_MAILMAN_LOCAL_DOMAIN)
        else:
            localaddr = k
        # Format the text file nicely
        print >> fp, fqdnaddr, ((fieldsz - len(k)) * ' '), localaddr
    # Finish the text file stanza
    print >> fp, '# STANZA END:', listname
    print >> fp