def changeMemberAddress(self, member, newaddress, nodelete=0):
     assert self.__mlist.Locked()
     # Make sure the old address is a member.  Assertions that the new
     # address is not already a member is done by addNewMember() below.
     self.__assertIsMember(member)
     # Get the old values
     memberkey = member.lower()
     fullname = self.getMemberName(memberkey)
     flags = self.__mlist.user_options.get(memberkey, 0)
     digestsp = self.getMemberOption(memberkey, mm_cfg.Digests)
     password = self.__mlist.passwords.get(memberkey,
                                           Utils.MakeRandomPassword())
     lang = self.getMemberLanguage(memberkey)
     delivery = self.__mlist.delivery_status.get(member.lower(),
                                                 (MemberAdaptor.ENABLED, 0))
     # First, possibly delete the old member
     if not nodelete:
         self.removeMember(memberkey)
     # Now, add the new member
     self.addNewMember(newaddress,
                       realname=fullname,
                       digest=digestsp,
                       password=password,
                       language=lang)
     # Set the entire options bitfield
     if flags:
         self.__mlist.user_options[newaddress.lower()] = flags
     # If this is a straightforward address change, i.e. nodelete = 0,
     # preserve the delivery status and time if BYUSER or BYADMIN
     if delivery[0] in (MemberAdaptor.BYUSER, MemberAdaptor.BYADMIN)\
       and not nodelete:
         self.__mlist.delivery_status[newaddress.lower()] = delivery
Пример #2
0
        def addNewMember(self, member, **kws):
    #        assert self.__mlist.Locked()
            # Make sure this address isn't already a member
            if self.isMember(member):
                raise Errors.MMAlreadyAMember, member
            # Parse the keywords
            digest = 0
            password = Utils.MakeRandomPassword()
            language = self.__mlist.preferred_language
            realname = None
            if kws.has_key('digest'):
                digest = kws['digest']
                del kws['digest']
            if kws.has_key('password'):
                password = kws['password']
                del kws['password']
            if kws.has_key('language'):
                language = kws['language']
                del kws['language']
            if kws.has_key('realname'):
                realname = kws['realname']
                del kws['realname']
            # Assert that no other keywords are present
            if kws:
                raise ValueError, kws.keys()
            # If the localpart has uppercase letters in it, then the value in the
            # members (or digest_members) dict is the case preserved address.
            # Otherwise the value is 0.  Note that the case of the domain part is
            # of course ignored.
            if Utils.LCDomain(member) == member.lower():
                value = 0
            else:
                value = member
                member = member.lower()
            if digest:
                digest = 'Y'
            else:
                digest = 'N'
            # All we need to do here is add the address.
            # and Set the member's default set of options
            if self.__mlist.new_member_options:
                options = self.__mlist.new_member_options
            else:
                options = 0
            query = "INSERT INTO %s " \
                + "(address, user_options, password, lang, " \
                + "digest, delivery_status,listname) values " \
                + "('%s',%s,'%s','%s','%s','%s','%s')"
            query = query %( self._table,
                self.escape(member), options, md5_new(password).hexdigest(),
                language, digest, MemberAdaptor.ENABLED,self.__mlist.internal_name())
            if mm_cfg.MYSQL_MEMBER_DB_VERBOSE:
                syslog('mysql',query)
	    mm_cfg.cursor.execute(query)
	    mm_cfg.connection.commit()
            if realname:
                self.setMemberName(member, realname)
Пример #3
0
def apply_mailman_changes(daan, changes):
    mlo = {}

    def ensure_opened(l):
        if l in mlo:
            return True
        try:
            mlo[l] = MailList.MailList(l)
            return True
        except Errors.MMUnknownListError:
            logging.warn("mailman: could not open %s" % l)
        return False

    for name, humanName in changes['create']:
        newlist = os.path.join(settings.MAILMAN_PATH, 'bin/newlist')
        ret = call([
            newlist, '-q', name, settings.MAILMAN_DEFAULT_OWNER,
            settings.MAILMAN_DEFAULT_PASSWORD
        ])
        if ret != 0:
            logging.error("bin/newlist failed")
            continue
        # Our custom settings
        # from: http://karpenoktem.com/wiki/WebCie:Mailinglist
        ensure_opened(name)
        ml = mlo[name]
        ml.send_reminders = 0
        ml.send_welcome_msg = False
        ml.max_message_size = 0
        ml.subscribe_policy = 3
        ml.unsubscribe_policy = 0
        ml.private_roster = 2
        ml.generic_nonmember_action = 0
        ml.require_explicit_destination = 0
        ml.max_num_recipients = 0
        ml.archive_private = 1
        ml.from_is_list = 1
    try:
        for l in changes['add']:
            if not ensure_opened(l):
                continue
            for em in changes['add'][l]:
                pw = Utils.MakeRandomPassword()
                desc = UserDesc.UserDesc(em, '', pw, False)
                mlo[l].ApprovedAddMember(desc, False, False)
        for l in changes['remove']:
            if not ensure_opened(l):
                continue
            for em in changes['remove'][l]:
                mlo[l].ApprovedDeleteMember(em,
                                            admin_notif=False,
                                            userack=False)
    finally:
        for ml in mlo.values():
            ml.Save()
            ml.Unlock()
Пример #4
0
def add_list(name, **kwargs):
    '''
    Adds a list to the server

    Arguments:
     - name: The desired name of the list
     - owner: The owner of the list. If not set, [email protected] is used
     - password: A given password for the list. If empty, a random password will be set.
     - language: Language of list (optional)
     - urlhost: Urlhost of the list (optional)
     - emailhost: Run list under other email-domain (optional)

    Returns true or false
    '''

    # If list is already present, exit
    if list_present(name):
        return False, 'List is already present on this system'

    owner = DEFAULT_OWNER
    if 'owner' in kwargs:
        owner = kwargs['owner']

    if 'password' in kwargs:
        password = kwargs['password']
    else:
        password = Utils.MakeRandomPassword(mm_cfg.ADMIN_PASSWORD_LENGTH)

    # Add optional arguments
    args = ''
    if 'language' in kwargs:
        args = args + " -l %s" % kwargs['language']

    if 'urlhost' in kwargs:
        args = args + " -u %s" % kwargs['urlhost']

    if 'emailhost' in kwargs:
        args = args + " -e %s" % kwargs['emailhost']

    # We use the commandline tools, because they do a lot of checks,
    # we don't want to reimplement all of them.
    if len(args) > 0:
        cmdline = "%s/newlist %s %s %s %s" % (MM_PATH, args.strip(), name,
                                              owner, password)
    else:
        cmdline = "%s/newlist %s %s %s" % (MM_PATH, name, owner, password)

    cmd = cmdline.split(' ')
    p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE)
    p.communicate('\n')
    p.wait()
    if p.returncode == 0:
        return True
    return False, 'Could not add list %s, something went wrong' % name
Пример #5
0
 def MailUserPassword(self, user):
     listfullname = '%s@%s' % (self.real_name, self.host_name)
     requestaddr = self.GetRequestEmail()
     # find the lowercased version of the user's address
     adminaddr = self.GetBouncesEmail()
     assert self.isMember(user)
     if not self.getMemberPassword(user):
         # The user's password somehow got corrupted.  Generate a new one
         # for him, after logging this bogosity.
         syslog('error', 'User %s had a false password for list %s', user,
                self.internal_name())
         waslocked = self.Locked()
         if not waslocked:
             self.Lock()
         try:
             self.setMemberPassword(user, Utils.MakeRandomPassword())
             self.Save()
         finally:
             if not waslocked:
                 self.Unlock()
     # Now send the user his password
     cpuser = self.getMemberCPAddress(user)
     recipient = self.GetMemberAdminEmail(cpuser)
     subject = _('%(listfullname)s mailing list reminder')
     # Get user's language and charset
     lang = self.getMemberLanguage(user)
     cset = Utils.GetCharSet(lang)
     password = self.getMemberPassword(user)
     # TK: Make unprintables to ?
     # The list owner should allow users to set language options if they
     # want to use non-us-ascii characters in password and send it back.
     password = unicode(password, cset, 'replace').encode(cset, 'replace')
     # get the text from the template
     text = Utils.maketext(
         'userpass.txt', {
             'user': cpuser,
             'listname': self.real_name,
             'fqdn_lname': self.GetListEmail(),
             'password': password,
             'options_url': self.GetOptionsURL(user, absolute=True),
             'requestaddr': requestaddr,
             'owneraddr': self.GetOwnerEmail(),
         },
         lang=lang,
         mlist=self)
     msg = Message.UserNotification(recipient, adminaddr, subject, text,
                                    lang)
     msg['X-No-Archive'] = 'yes'
     msg.send(self, verp=mm_cfg.VERP_PERSONALIZED_DELIVERIES)
    def addNewMember(self, member, **kws):
        assert self.__mlist.Locked()
        # Make sure this address isn't already a member
        if self.isMember(member):
            raise Errors.MMAlreadyAMember(member)
        # Parse the keywords
        digest = 0
        password = Utils.MakeRandomPassword()
        language = self.__mlist.preferred_language
        realname = None
        if 'digest' in kws:
            digest = kws['digest']
            del kws['digest']
        if 'password' in kws:
            password = kws['password']
            del kws['password']
        if 'language' in kws:
            language = kws['language']
            del kws['language']
        if 'realname' in kws:
            realname = kws['realname']
            del kws['realname']
        # Assert that no other keywords are present
        if kws:
            raise ValueError(list(kws.keys()))
        # If the localpart has uppercase letters in it, then the value in the
        # members (or digest_members) dict is the case preserved address.
        # Otherwise the value is 0.  Note that the case of the domain part is
        # of course ignored.
        if Utils.LCDomain(member) == member.lower():
            value = 0
        else:
            value = member
        member = member.lower()
        if digest:
            self.__mlist.digest_members[member] = value
        else:
            self.__mlist.members[member] = value
        self.setMemberPassword(member, password)

        self.setMemberLanguage(member, language)
        if realname:
            self.setMemberName(member, realname)
        # Set the member's default set of options
        if self.__mlist.new_member_options:
            self.__mlist.user_options[member] = self.__mlist.new_member_options
Пример #7
0
def reset_pw(mlist, *args):
    try:
        opts, args = getopt.getopt(args, 'v', ['verbose'])
    except getopt.error as msg:
        usage(1, msg)

    verbose = False
    for opt, args in opts:
        if opt in ('-v', '--verbose'):
            verbose = True

    listname = mlist.internal_name()
    if verbose:
        print(C_('Changing passwords for list: %(listname)s'))

    for member in mlist.getMembers():
        randompw = Utils.MakeRandomPassword()
        mlist.setMemberPassword(member, randompw)
        if verbose:
            print(C_('New password for member %(member)40s: %(randompw)s'))

    mlist.Save()
Пример #8
0
    _('There was no hidden token in your submission or it was corrupted.'))
            results.append(_('You must GET the form before submitting it.'))
    # Check captcha
    captcha_answer = cgidata.getvalue('captcha_answer', '')
    if not Captcha.verify(fcaptcha_idx, captcha_answer, mm_cfg.CAPTCHAS):
        results.append(_('This was not the right answer to the CAPTCHA question.'))
    # Was an attempt made to subscribe the list to itself?
    if email == mlist.GetListEmail():
        syslog('mischief', 'Attempt to self subscribe %s: %s', email, remote)
        results.append(_('You may not subscribe a list to itself!'))
    # If the user did not supply a password, generate one for him
    password = cgidata.getfirst('pw', '').strip()
    confirmed = cgidata.getfirst('pw-conf', '').strip()

    if not password and not confirmed:
        password = Utils.MakeRandomPassword()
    elif not password or not confirmed:
        results.append(_('If you supply a password, you must confirm it.'))
    elif password <> confirmed:
        results.append(_('Your passwords did not match.'))

    # Get the digest option for the subscription.
    digestflag = cgidata.getfirst('digest')
    if digestflag:
        try:
            digest = int(digestflag)
        except (TypeError, ValueError):
            digest = 0
    else:
        digest = mlist.digest_is_default
Пример #9
0
def process_form(mlist, doc, cgidata, lang):
    listowner = mlist.GetOwnerEmail()
    realname = mlist.real_name
    results = []

    # The email address being subscribed, required
    email = cgidata.getvalue('email', '').strip()
    if not email:
        results.append(_('You must supply a valid email address.'))

    fullname = cgidata.getvalue('fullname', '')
    # Canonicalize the full name
    fullname = Utils.canonstr(fullname, lang)
    # Who was doing the subscribing?
    remote = os.environ.get(
        'REMOTE_HOST', os.environ.get('REMOTE_ADDR', 'unidentified origin'))
    # Are we checking the hidden data?
    if mm_cfg.SUBSCRIBE_FORM_SECRET:
        now = int(time.time())
        try:
            ftime, fhash = cgidata.getvalue('sub_form_token', '').split(':')
            then = int(ftime)
        except ValueError:
            ftime = fhash = ''
            then = now
        token = Utils.sha_new(mm_cfg.SUBSCRIBE_FORM_SECRET + ftime +
                              mlist.internal_name() + remote).hexdigest()
        if now - then > mm_cfg.FORM_LIFETIME:
            results.append(_('The form is too old.  Please GET it again.'))
        if now - then < mm_cfg.SUBSCRIBE_FORM_MIN_TIME:
            results.append(
                _('Please take a few seconds to fill out the form before submitting it.'
                  ))
        if token != fhash:
            results.append(_('You must GET the form before submitting it.'))
    # Was an attempt made to subscribe the list to itself?
    if email == mlist.GetListEmail():
        syslog('mischief', 'Attempt to self subscribe %s: %s', email, remote)
        results.append(_('You may not subscribe a list to itself!'))
    # If the user did not supply a password, generate one for him
    password = cgidata.getvalue('pw', '').strip()
    confirmed = cgidata.getvalue('pw-conf', '').strip()

    if not password and not confirmed:
        password = Utils.MakeRandomPassword()
    elif not password or not confirmed:
        results.append(_('If you supply a password, you must confirm it.'))
    elif password <> confirmed:
        results.append(_('Your passwords did not match.'))

    # Get the digest option for the subscription.
    digestflag = cgidata.getvalue('digest')
    if digestflag:
        try:
            digest = int(digestflag)
        except ValueError:
            digest = 0
    else:
        digest = mlist.digest_is_default

    # Sanity check based on list configuration.  BAW: It's actually bogus that
    # the page allows you to set the digest flag if you don't really get the
    # choice. :/
    if not mlist.digestable:
        digest = 0
    elif not mlist.nondigestable:
        digest = 1

    if results:
        print_results(mlist, ERRORSEP.join(results), doc, lang)
        return

    # If this list has private rosters, we have to be careful about the
    # message that gets printed, otherwise the subscription process can be
    # used to mine for list members.  It may be inefficient, but it's still
    # possible, and that kind of defeats the purpose of private rosters.
    # We'll use this string for all successful or unsuccessful subscription
    # results.
    if mlist.private_roster == 0:
        # Public rosters
        privacy_results = ''
    else:
        privacy_results = _("""\
Your subscription request has been received, and will soon be acted upon.
Depending on the configuration of this mailing list, your subscription request
may have to be first confirmed by you via email, or approved by the list
moderator.  If confirmation is required, you will soon get a confirmation
email which contains further instructions.""")

    try:
        userdesc = UserDesc(email, fullname, password, digest, lang)
        mlist.AddMember(userdesc, remote)
        results = ''
    # Check for all the errors that mlist.AddMember can throw options on the
    # web page for this cgi
    except Errors.MembershipIsBanned:
        results = _("""The email address you supplied is banned from this
        mailing list.  If you think this restriction is erroneous, please
        contact the list owners at %(listowner)s.""")
    except Errors.MMBadEmailError:
        results = _("""\
The email address you supplied is not valid.  (E.g. it must contain an
`@'.)""")
    except Errors.MMHostileAddress:
        results = _("""\
Your subscription is not allowed because the email address you gave is
insecure.""")
    except Errors.MMSubscribeNeedsConfirmation:
        # Results string depends on whether we have private rosters or not
        if privacy_results:
            results = privacy_results
        else:
            results = _("""\
Confirmation from your email address is required, to prevent anyone from
subscribing you without permission.  Instructions are being sent to you at
%(email)s.  Please note your subscription will not start until you confirm
your subscription.""")
    except Errors.MMNeedApproval, x:
        # Results string depends on whether we have private rosters or not
        if privacy_results:
            results = privacy_results
        else:
            # We need to interpolate into x.__str__()
            x = _(str(x))
            results = _("""\
Your subscription request was deferred because %(x)s.  Your request has been
forwarded to the list moderator.  You will receive email informing you of the
moderator's decision when they get to your request.""")
Пример #10
0
        print >> fd, msg
    sys.exit(code)


def reset_pw(mlist, *args):
    try:
        opts, args = getopt.getopt(args, 'v', ['verbose'])
    except getopt.error, msg:
        usage(1, msg)

    verbose = False
    for opt, args in opts:
        if opt in ('-v', '--verbose'):
            verbose = True

    listname = mlist.internal_name()
    if verbose:
        print _('Changing passwords for list: %(listname)s')

    for member in mlist.getMembers():
        randompw = Utils.MakeRandomPassword()
        mlist.setMemberPassword(member, randompw)
        if verbose:
            print _('New password for member %(member)40s: %(randompw)s')

    mlist.Save()


if __name__ == '__main__':
    usage(0)
def process(res, args):
    mlist = res.mlist
    digest = None
    password = None
    address = None
    realname = None
    # Parse the args
    argnum = 0
    for arg in args:
        if arg.lower().startswith('address='):
            address = arg[8:]
        elif argnum == 0:
            password = arg
        elif argnum == 1:
            if arg.lower() not in ('digest', 'nodigest'):
                res.results.append(_('Bad digest specifier: %(arg)s'))
                return STOP
            if arg.lower() == 'digest':
                digest = 1
            else:
                digest = 0
        else:
            res.results.append(_('Usage:'))
            res.results.append(gethelp(mlist))
            return STOP
        argnum += 1
    # Fix the password/digest issue
    if (digest is None
            and password and password.lower() in ('digest', 'nodigest')):
        if password.lower() == 'digest':
            digest = 1
        else:
            digest = 0
        password = None
    # Fill in empty defaults
    if digest is None:
        digest = mlist.digest_is_default
    if password is None:
        password = Utils.MakeRandomPassword()
    if address is None:
        realname, address = parseaddr(res.msg['from'])
        if not address:
            # Fall back to the sender address
            address = res.msg.get_sender()
        if not address:
            res.results.append(_('No valid address found to subscribe'))
            return STOP
        # Watch for encoded names
        try:
            h = make_header(decode_header(realname))
            # BAW: in Python 2.2, use just unicode(h)
            realname = str(h)
        except UnicodeError:
            realname = ''
        # Coerce to byte string if uh contains only ascii
        try:
            realname = realname.encode('us-ascii')
        except UnicodeError:
            pass
    # Create the UserDesc record and do a non-approved subscription
    listowner = mlist.GetOwnerEmail()
    userdesc = UserDesc(address, realname, password, digest)
    remote = res.msg.get_sender()
    try:
        mlist.AddMember(userdesc, remote)
    except Errors.MembershipIsBanned:
        res.results.append(_("""\
The email address you supplied is banned from this mailing list.
If you think this restriction is erroneous, please contact the list
owners at %(listowner)s."""))
        return STOP
    except Errors.MMBadEmailError:
        res.results.append(_("""\
Mailman won't accept the given email address as a valid address.
(E.g. it must have an @ in it.)"""))
        return STOP
    except Errors.MMHostileAddress:
        res.results.append(_("""\
Your subscription is not allowed because
the email address you gave is insecure."""))
        return STOP
    except Errors.MMAlreadyAMember:
        res.results.append(_('You are already subscribed!'))
        return STOP
    except Errors.MMCantDigestError:
        res.results.append(
            _('No one can subscribe to the digest of this list!'))
        return STOP
    except Errors.MMMustDigestError:
        res.results.append(_('This list only supports digest subscriptions!'))
        return STOP
    except Errors.MMSubscribeNeedsConfirmation:
        # We don't need to respond /and/ send a confirmation message.
        res.respond = 0
    except Errors.MMNeedApproval:
        res.results.append(_("""\
Your subscription request has been forwarded to the list administrator
at %(listowner)s for review."""))
    else:
        # Everything is a-ok
        res.results.append(_('Subscription request succeeded.'))
Пример #12
0
def process_form(mlist, doc, cgidata, lang):
    listowner = mlist.GetOwnerEmail()
    realname = mlist.real_name
    results = []

    # The email address being subscribed, required
    email = cgidata.getfirst('email', '').strip()
    if not email:
        results.append(_('You must supply a valid email address.'))

    fullname = cgidata.getfirst('fullname', '')
    # Canonicalize the full name
    fullname = Utils.canonstr(fullname, lang)
    # Who was doing the subscribing?
    remote = os.environ.get('HTTP_FORWARDED_FOR',
             os.environ.get('HTTP_X_FORWARDED_FOR',
             os.environ.get('REMOTE_ADDR',
                            'unidentified origin')))

    # Check reCAPTCHA submission, if enabled
    if mm_cfg.RECAPTCHA_SECRET_KEY:
        request = urllib2.Request(
            url = 'https://www.google.com/recaptcha/api/siteverify',
            data = urllib.urlencode({
                'secret': mm_cfg.RECAPTCHA_SECRET_KEY,
                'response': cgidata.getvalue('g-recaptcha-response', ''),
                'remoteip': remote}))
        try:
            httpresp = urllib2.urlopen(request)
            captcha_response = json.load(httpresp)
            httpresp.close()
            if not captcha_response['success']:
                e_codes = COMMASPACE.join(captcha_response['error-codes'])
                results.append(_('reCAPTCHA validation failed: %(e_codes)s'))
        except urllib2.URLError as e:
            e_reason = e.reason
            results.append(_('reCAPTCHA could not be validated: %(e_reason)s'))

    # Are we checking the hidden data?
    if mm_cfg.SUBSCRIBE_FORM_SECRET:
        now = int(time.time())
        # Try to accept a range in case of load balancers, etc.  (LP: #1447445)
        if remote.find('.') >= 0:
            # ipv4 - drop last octet
            remote1 = remote.rsplit('.', 1)[0]
        else:
            # ipv6 - drop last 16 (could end with :: in which case we just
            #        drop one : resulting in an invalid format, but it's only
            #        for our hash so it doesn't matter.
            remote1 = remote.rsplit(':', 1)[0]
        try:
            ftime, fhash = cgidata.getfirst('sub_form_token', '').split(':')
            then = int(ftime)
        except ValueError:
            ftime = fhash = ''
            then = 0
        token = Utils.sha_new(mm_cfg.SUBSCRIBE_FORM_SECRET +
                              ftime +
                              mlist.internal_name() +
                              remote1).hexdigest()
        if ftime and now - then > mm_cfg.FORM_LIFETIME:
            results.append(_('The form is too old.  Please GET it again.'))
        if ftime and now - then < mm_cfg.SUBSCRIBE_FORM_MIN_TIME:
            results.append(
    _('Please take a few seconds to fill out the form before submitting it.'))
        if ftime and token != fhash:
            results.append(
                _("The hidden token didn't match.  Did your IP change?"))
        if not ftime:
            results.append(
    _('There was no hidden token in your submission or it was corrupted.'))
            results.append(_('You must GET the form before submitting it.'))
    # Was an attempt made to subscribe the list to itself?
    if email == mlist.GetListEmail():
        syslog('mischief', 'Attempt to self subscribe %s: %s', email, remote)
        results.append(_('You may not subscribe a list to itself!'))
    # If the user did not supply a password, generate one for him
    password = cgidata.getfirst('pw', '').strip()
    confirmed = cgidata.getfirst('pw-conf', '').strip()

    if not password and not confirmed:
        password = Utils.MakeRandomPassword()
    elif not password or not confirmed:
        results.append(_('If you supply a password, you must confirm it.'))
    elif password <> confirmed:
        results.append(_('Your passwords did not match.'))

    # Get the digest option for the subscription.
    digestflag = cgidata.getfirst('digest')
    if digestflag:
        try:
            digest = int(digestflag)
        except (TypeError, ValueError):
            digest = 0
    else:
        digest = mlist.digest_is_default

    # Sanity check based on list configuration.  BAW: It's actually bogus that
    # the page allows you to set the digest flag if you don't really get the
    # choice. :/
    if not mlist.digestable:
        digest = 0
    elif not mlist.nondigestable:
        digest = 1

    if results:
        print_results(mlist, ERRORSEP.join(results), doc, lang)
        return

    # If this list has private rosters, we have to be careful about the
    # message that gets printed, otherwise the subscription process can be
    # used to mine for list members.  It may be inefficient, but it's still
    # possible, and that kind of defeats the purpose of private rosters.
    # We'll use this string for all successful or unsuccessful subscription
    # results.
    if mlist.private_roster == 0:
        # Public rosters
        privacy_results = ''
    else:
        privacy_results = _("""\
Your subscription request has been received, and will soon be acted upon.
Depending on the configuration of this mailing list, your subscription request
may have to be first confirmed by you via email, or approved by the list
moderator.  If confirmation is required, you will soon get a confirmation
email which contains further instructions.""")

    try:
        userdesc = UserDesc(email, fullname, password, digest, lang)
        mlist.AddMember(userdesc, remote)
        results = ''
    # Check for all the errors that mlist.AddMember can throw options on the
    # web page for this cgi
    except Errors.MembershipIsBanned:
        results = _("""The email address you supplied is banned from this
        mailing list.  If you think this restriction is erroneous, please
        contact the list owners at %(listowner)s.""")
    except Errors.MMBadEmailError:
        results = _("""\
The email address you supplied is not valid.  (E.g. it must contain an
`@'.)""")
    except Errors.MMHostileAddress:
        results = _("""\
Your subscription is not allowed because the email address you gave is
insecure.""")
    except Errors.MMSubscribeNeedsConfirmation:
        # Results string depends on whether we have private rosters or not
        if privacy_results:
            results = privacy_results
        else:
            results = _("""\
Confirmation from your email address is required, to prevent anyone from
subscribing you without permission.  Instructions are being sent to you at
%(email)s.  Please note your subscription will not start until you confirm
your subscription.""")
    except Errors.MMNeedApproval, x:
        # Results string depends on whether we have private rosters or not
        if privacy_results:
            results = privacy_results
        else:
            # We need to interpolate into x.__str__()
            x = _(str(x))
            results = _("""\
Your subscription request was deferred because %(x)s.  Your request has been
forwarded to the list moderator.  You will receive email informing you of the
moderator's decision when they get to your request.""")