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)
    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
示例#3
0
def UpdateOldVars(l, stored_state):
    """Transform old variable values into new ones, deleting old ones.
    stored_state is last snapshot from file, as opposed to from InitVars()."""
    def PreferStored(oldname,
                     newname,
                     newdefault=uniqueval,
                     l=l,
                     state=stored_state):
        """Use specified old value if new value is not in stored state.

        If the old attr does not exist, and no newdefault is specified, the
        new attr is *not* created - so either specify a default or be positive
        that the old attr exists - or don't depend on the new attr.

        """
        if hasattr(l, oldname):
            if not state.has_key(newname):
                setattr(l, newname, getattr(l, oldname))
            delattr(l, oldname)
        if not hasattr(l, newname) and newdefault is not uniqueval:
            setattr(l, newname, newdefault)

    def recode(mlist, f, t):
        """If the character set for a list's preferred_language has changed,
        attempt to recode old string values into the new character set.

        mlist is the list, f is the old charset and t is the new charset.
        """
        for x in dir(mlist):
            if x.startswith('_'):
                continue
            nv = doitem(getattr(mlist, x), f, t)
            if nv:
                setattr(mlist, x, nv)

    def doitem(v, f, t):
        """Recursively process lists, tuples and dictionary values and
        convert strings as needed. Return either the updated item or None
        if no change."""
        changed = False
        if isinstance(v, str):
            return convert(v, f, t)
        elif isinstance(v, list):
            for i in range(len(v)):
                nv = doitem(v[i], f, t)
                if nv:
                    changed = True
                    v[i] = nv
            if changed:
                return v
            else:
                return None
        elif isinstance(v, tuple):
            nt = ()
            for i in range(len(v)):
                nv = doitem(v[i], f, t)
                if nv:
                    changed = True
                    nt += (nv, )
                else:
                    nt += (v[i], )
            if changed:
                return nt
            else:
                return None
        elif isinstance(v, dict):
            for k, ov in v.items():
                nv = doitem(ov, f, t)
                if nv:
                    changed = True
                    v[k] = nv
            if changed:
                return v
            else:
                return None
        else:
            return None

    def convert(s, f, t):
        """This does the actual character set conversion of the string s
        from charset f to charset t."""

        try:
            u = unicode(s, f)
            is_f = True
        except ValueError:
            is_f = False
        try:
            unicode(s, t)
            is_t = True
        except ValueError:
            is_t = False
        if is_f and not is_t:
            return u.encode(t, 'replace')
        else:
            return None

    # Migrate to 2.1b3, baw 17-Aug-2001
    if hasattr(l, 'dont_respond_to_post_requests'):
        oldval = getattr(l, 'dont_respond_to_post_requests')
        if not hasattr(l, 'respond_to_post_requests'):
            l.respond_to_post_requests = not oldval
        del l.dont_respond_to_post_requests

    # Migrate to 2.1b3, baw 13-Oct-2001
    # Basic defaults for new variables
    if not hasattr(l, 'default_member_moderation'):
        l.default_member_moderation = mm_cfg.DEFAULT_DEFAULT_MEMBER_MODERATION
    if not hasattr(l, 'accept_these_nonmembers'):
        l.accept_these_nonmembers = []
    if not hasattr(l, 'hold_these_nonmembers'):
        l.hold_these_nonmembers = []
    if not hasattr(l, 'reject_these_nonmembers'):
        l.reject_these_nonmembers = []
    if not hasattr(l, 'discard_these_nonmembers'):
        l.discard_these_nonmembers = []
    if not hasattr(l, 'forward_auto_discards'):
        l.forward_auto_discards = mm_cfg.DEFAULT_FORWARD_AUTO_DISCARDS
    if not hasattr(l, 'generic_nonmember_action'):
        l.generic_nonmember_action = mm_cfg.DEFAULT_GENERIC_NONMEMBER_ACTION
    # Now convert what we can...  Note that the interaction between the
    # MM2.0.x attributes `moderated', `member_posting_only', and `posters' is
    # so confusing, it makes my brain really ache.  Which is why they go away
    # in MM2.1.  I think the best we can do semantically is the following:
    #
    # - If moderated == yes, then any sender who's address is not on the
    #   posters attribute would get held for approval.  If the sender was on
    #   the posters list, then we'd defer judgement to a later step
    # - If member_posting_only == yes, then members could post without holds,
    #   and if there were any addresses added to posters, they could also post
    #   without holds.
    # - If member_posting_only == no, then what happens depends on the value
    #   of the posters attribute:
    #       o If posters was empty, then anybody can post without their
    #         message being held for approval
    #       o If posters was non-empty, then /only/ those addresses could post
    #         without approval, i.e. members not on posters would have their
    #         messages held for approval.
    #
    # How to translate this mess to MM2.1 values?  I'm sure I got this wrong
    # before, but here's how we're going to do it, as of MM2.1b3.
    #
    # - We'll control member moderation through their Moderate flag, and
    #   non-member moderation through the generic_nonmember_action,
    #   hold_these_nonmembers, and accept_these_nonmembers.
    # - If moderated == yes then we need to troll through the addresses on
    #   posters, and any non-members would get added to
    #   accept_these_nonmembers.  /Then/ we need to troll through the
    #   membership and any member on posters would get their Moderate flag
    #   unset, while members not on posters would get their Moderate flag set.
    #   Then generic_nonmember_action gets set to 1 (hold) so nonmembers get
    #   moderated, and default_member_moderation will be set to 1 (hold) so
    #   new members will also get held for moderation.  We'll stop here.
    # - We only get to here if moderated == no.
    # - If member_posting_only == yes, then we'll turn off the Moderate flag
    #   for members.  We troll through the posters attribute and add all those
    #   addresses to accept_these_nonmembers.  We'll also set
    #   generic_nonmember_action to 1 and default_member_moderation to 0.
    #   We'll stop here.
    # - We only get to here if member_posting_only == no
    # - If posters is empty, then anybody could post without being held for
    #   approval, so we'll set generic_nonmember_action to 0 (accept), and
    #   we'll turn off the Moderate flag for all members.  We'll also turn off
    #   default_member_moderation so new members can post without approval.
    #   We'll stop here.
    # - We only get here if posters is non-empty.
    # - This means that /only/ the addresses on posters got to post without
    #   being held for approval.  So first, we troll through posters and add
    #   all non-members to accept_these_nonmembers.  Then we troll through the
    #   membership and if their address is on posters, we'll clear their
    #   Moderate flag, otherwise we'll set it.  We'll turn on
    #   default_member_moderation so new members get moderated.  We'll set
    #   generic_nonmember_action to 1 (hold) so all other non-members will get
    #   moderated.  And I think we're finally done.
    #
    # SIGH.
    if hasattr(l, 'moderated'):
        # We'll assume we're converting all these attributes at once
        if l.moderated:
            #syslog('debug', 'Case 1')
            for addr in l.posters:
                if not l.isMember(addr):
                    l.accept_these_nonmembers.append(addr)
            for member in l.getMembers():
                l.setMemberOption(
                    member,
                    mm_cfg.Moderate,
                    # reset for explicitly named members
                    member not in l.posters)
            l.generic_nonmember_action = 1
            l.default_member_moderation = 1
        elif l.member_posting_only:
            #syslog('debug', 'Case 2')
            for addr in l.posters:
                if not l.isMember(addr):
                    l.accept_these_nonmembers.append(addr)
            for member in l.getMembers():
                l.setMemberOption(member, mm_cfg.Moderate, 0)
            l.generic_nonmember_action = 1
            l.default_member_moderation = 0
        elif not l.posters:
            #syslog('debug', 'Case 3')
            for member in l.getMembers():
                l.setMemberOption(member, mm_cfg.Moderate, 0)
            l.generic_nonmember_action = 0
            l.default_member_moderation = 0
        else:
            #syslog('debug', 'Case 4')
            for addr in l.posters:
                if not l.isMember(addr):
                    l.accept_these_nonmembers.append(addr)
            for member in l.getMembers():
                l.setMemberOption(
                    member,
                    mm_cfg.Moderate,
                    # reset for explicitly named members
                    member not in l.posters)
            l.generic_nonmember_action = 1
            l.default_member_moderation = 1
        # Now get rid of the old attributes
        del l.moderated
        del l.posters
        del l.member_posting_only
    if hasattr(l, 'forbidden_posters'):
        # For each of the posters on this list, if they are members, toggle on
        # their moderation flag.  If they are not members, then add them to
        # hold_these_nonmembers.
        forbiddens = l.forbidden_posters
        for addr in forbiddens:
            if l.isMember(addr):
                l.setMemberOption(addr, mm_cfg.Moderate, 1)
            else:
                l.hold_these_nonmembers.append(addr)
        del l.forbidden_posters

    # Migrate to 1.0b6, klm 10/22/1998:
    PreferStored('reminders_to_admins', 'umbrella_list',
                 mm_cfg.DEFAULT_UMBRELLA_LIST)

    # Migrate up to 1.0b5:
    PreferStored('auto_subscribe', 'open_subscribe')
    PreferStored('closed', 'private_roster')
    PreferStored('mimimum_post_count_before_removal',
                 'mimimum_post_count_before_bounce_action')
    PreferStored('bad_posters', 'forbidden_posters')
    PreferStored('automatically_remove', 'automatic_bounce_action')
    if hasattr(l, "open_subscribe"):
        if l.open_subscribe:
            if mm_cfg.ALLOW_OPEN_SUBSCRIBE:
                l.subscribe_policy = 0
            else:
                l.subscribe_policy = 1
        else:
            l.subscribe_policy = 2  # admin approval
        delattr(l, "open_subscribe")
    if not hasattr(l, "administrivia"):
        setattr(l, "administrivia", mm_cfg.DEFAULT_ADMINISTRIVIA)
    if not hasattr(l, "admin_member_chunksize"):
        setattr(l, "admin_member_chunksize",
                mm_cfg.DEFAULT_ADMIN_MEMBER_CHUNKSIZE)
    #
    # this attribute was added then deleted, so there are a number of
    # cases to take care of
    #
    if hasattr(l, "posters_includes_members"):
        if l.posters_includes_members:
            if l.posters:
                l.member_posting_only = 1
        else:
            if l.posters:
                l.member_posting_only = 0
        delattr(l, "posters_includes_members")
    elif l.data_version <= 10 and l.posters:
        # make sure everyone gets the behavior the list used to have, but only
        # for really old versions of Mailman (1.0b5 or before).  Any newer
        # version of Mailman should not get this attribute whacked.
        l.member_posting_only = 0
    #
    # transfer the list data type for holding members and digest members
    # to the dict data type starting file format version 11
    #
    if type(l.members) is ListType:
        members = {}
        for m in l.members:
            members[m] = 1
        l.members = members
    if type(l.digest_members) is ListType:
        dmembers = {}
        for dm in l.digest_members:
            dmembers[dm] = 1
        l.digest_members = dmembers
    #
    # set admin_notify_mchanges
    #
    if not hasattr(l, "admin_notify_mchanges"):
        setattr(l, "admin_notify_mchanges",
                mm_cfg.DEFAULT_ADMIN_NOTIFY_MCHANGES)
    #
    # Convert the members and digest_members addresses so that the keys of
    # both these are always lowercased, but if there is a case difference, the
    # value contains the case preserved value
    #
    for k in l.members.keys():
        if k.lower() <> k:
            l.members[k.lower()] = Utils.LCDomain(k)
            del l.members[k]
        elif type(l.members[k]) == StringType and k == l.members[k].lower():
            # already converted
            pass
        else:
            l.members[k] = 0
    for k in l.digest_members.keys():
        if k.lower() <> k:
            l.digest_members[k.lower()] = Utils.LCDomain(k)
            del l.digest_members[k]
        elif type(l.digest_members[k]) == StringType and \
                 k == l.digest_members[k].lower():
            # already converted
            pass
        else:
            l.digest_members[k] = 0
    #
    # Convert pre 2.2 topics regexps which were compiled in verbose mode
    # to a non-verbose equivalent.
    #
    if stored_state['data_version'] < 106 and stored_state.has_key('topics'):
        l.topics = []
        for name, pattern, description, emptyflag in stored_state['topics']:
            pattern = Utils.strip_verbose_pattern(pattern)
            l.topics.append((name, pattern, description, emptyflag))
    #
    # Romanian and Russian had their character sets changed in 2.1.19
    # to utf-8. If there are any strings in the old encoding, try to recode
    # them.
    #
    if stored_state['data_version'] < 108:
        if l.preferred_language == 'ro':
            if Utils.GetCharSet('ro') == 'utf-8':
                recode(l, 'iso-8859-2', 'utf-8')
        if l.preferred_language == 'ru':
            if Utils.GetCharSet('ru') == 'utf-8':
                recode(l, 'koi8-r', 'utf-8')
    #
    # from_is_list was called author_is_list in 2.1.16rc2 (only).
    PreferStored('author_is_list', 'from_is_list', mm_cfg.DEFAULT_FROM_IS_LIST)
def main():
    global _
    doc = Document()
    doc.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE)

    method = Utils.GetRequestMethod()
    if method.lower() not in ('get', 'post'):
        title = _('CGI script error')
        doc.SetTitle(title)
        doc.AddItem(Header(2, title))
        doc.addError(_('Invalid request method: %(method)s'))
        doc.AddItem('<hr>')
        doc.AddItem(MailmanLogo())
        print('Status: 405 Method Not Allowed')
        print(doc.Format())
        return

    parts = Utils.GetPathPieces()
    lenparts = parts and len(parts)
    if not parts or lenparts < 1:
        title = _('CGI script error')
        doc.SetTitle(title)
        doc.AddItem(Header(2, title))
        doc.addError(_('Invalid options to CGI script.'))
        doc.AddItem('<hr>')
        doc.AddItem(MailmanLogo())
        print(doc.Format())
        return

    # get the list and user's name
    listname = parts[0].lower()
    # open list
    try:
        mlist = MailList.MailList(listname, lock=0)
    except Errors.MMListError as e:
        # Avoid cross-site scripting attacks
        safelistname = Utils.websafe(listname)
        title = _('CGI script error')
        doc.SetTitle(title)
        doc.AddItem(Header(2, title))
        doc.addError(_('No such list <em>%(safelistname)s</em>'))
        doc.AddItem('<hr>')
        doc.AddItem(MailmanLogo())
        # Send this with a 404 status.
        print('Status: 404 Not Found')
        print(doc.Format())
        syslog('error', 'options: No such list "%s": %s\n', listname, e)
        return

    # The total contents of the user's response
    cgidata = cgi.FieldStorage(keep_blank_values=1)

    # CSRF check
    safe_params = [
        'displang-button', 'language', 'email', 'password', 'login',
        'login-unsub', 'login-remind', 'VARHELP', 'UserOptions'
    ]
    try:
        params = list(cgidata.keys())
    except TypeError:
        # Someone crafted a POST with a bad Content-Type:.
        doc.AddItem(Header(2, _("Error")))
        doc.AddItem(Bold(_('Invalid options to CGI script.')))
        # Send this with a 400 status.
        print('Status: 400 Bad Request')
        print(doc.Format())
        return

    if set(params) - set(safe_params):
        csrf_checked = csrf_check(mlist, cgidata.getfirst('csrf_token'))
    else:
        csrf_checked = True
    # if password is present, void cookie to force password authentication.
    if cgidata.getfirst('password'):
        os.environ['HTTP_COOKIE'] = ''
        csrf_checked = True

    # Set the language for the page.  If we're coming from the listinfo cgi,
    # we might have a 'language' key in the cgi data.  That was an explicit
    # preference to view the page in, so we should honor that here.  If that's
    # not available, use the list's default language.
    language = cgidata.getfirst('language')
    if not Utils.IsLanguage(language):
        language = mlist.preferred_language
    i18n.set_language(language)
    doc.set_language(language)

    if lenparts < 2:
        user = cgidata.getfirst('email')
        if not user:
            # If we're coming from the listinfo page and we left the email
            # address field blank, it's not an error.  Likewise if we're
            # coming from anywhere else. Only issue the error if we came
            # via one of our buttons.
            if (cgidata.getfirst('login') or cgidata.getfirst('login-unsub')
                    or cgidata.getfirst('login-remind')):
                doc.addError(_('No address given'))
            loginpage(mlist, doc, None, language)
            print(doc.Format())
            return
    else:
        user = Utils.LCDomain(Utils.UnobscureEmail(SLASH.join(parts[1:])))
    # If a user submits a form or URL with post data or query fragments
    # with multiple occurrences of the same variable, we can get a list
    # here.  Be as careful as possible.
    if isinstance(user, list) or isinstance(user, tuple):
        if len(user) == 0:
            user = ''
        else:
            user = user[-1]

    # Avoid cross-site scripting attacks
    safeuser = Utils.websafe(user)
    try:
        Utils.ValidateEmail(user)
    except Errors.EmailAddressError:
        doc.addError(_('Illegal Email Address: %(safeuser)s'))
        loginpage(mlist, doc, None, language)
        print(doc.Format())
        return
    # Sanity check the user, but only give the "no such member" error when
    # using public rosters, otherwise, we'll leak membership information.
    if not mlist.isMember(user) and mlist.private_roster == 0:
        doc.addError(_('No such member: %(safeuser)s.'))
        loginpage(mlist, doc, None, language)
        print(doc.Format())
        return

    # Find the case preserved email address (the one the user subscribed with)
    lcuser = user.lower()
    try:
        cpuser = mlist.getMemberCPAddress(lcuser)
    except Errors.NotAMemberError:
        # This happens if the user isn't a member but we've got private rosters
        cpuser = None
    if lcuser == cpuser:
        cpuser = None

    # And now we know the user making the request, so set things up to for the
    # user's stored preferred language, overridden by any form settings for
    # their new language preference.
    userlang = cgidata.getfirst('language')
    if not Utils.IsLanguage(userlang):
        userlang = mlist.getMemberLanguage(user)
    doc.set_language(userlang)
    i18n.set_language(userlang)

    # Are we processing an unsubscription request from the login screen?
    msgc = _('If you are a list member, a confirmation email has been sent.')
    msga = _("""If you are a list member, your unsubscription request has been
             forwarded to the list administrator for approval.""")
    if 'login-unsub' in cgidata:
        # Because they can't supply a password for unsubscribing, we'll need
        # to do the confirmation dance.
        if mlist.isMember(user):
            # We must acquire the list lock in order to pend a request.
            try:
                mlist.Lock()
                # If unsubs require admin approval, then this request has to
                # be held.  Otherwise, send a confirmation.
                if mlist.unsubscribe_policy:
                    mlist.HoldUnsubscription(user)
                    doc.addError(msga, tag='')
                else:
                    ip = os.environ.get(
                        'HTTP_FORWARDED_FOR',
                        os.environ.get(
                            'HTTP_X_FORWARDED_FOR',
                            os.environ.get('REMOTE_ADDR',
                                           'unidentified origin')))
                    mlist.ConfirmUnsubscription(user, userlang, remote=ip)
                    doc.addError(msgc, tag='')
                mlist.Save()
            finally:
                mlist.Unlock()
        else:
            # Not a member
            if mlist.private_roster == 0:
                # Public rosters
                doc.addError(_('No such member: %(safeuser)s.'))
            else:
                syslog('mischief',
                       'Unsub attempt of non-member w/ private rosters: %s',
                       user)
                if mlist.unsubscribe_policy:
                    doc.addError(msga, tag='')
                else:
                    doc.addError(msgc, tag='')
        loginpage(mlist, doc, user, language)
        print(doc.Format())
        return

    # Are we processing a password reminder from the login screen?
    msg = _("""If you are a list member,
            your password has been emailed to you.""")
    if 'login-remind' in cgidata:
        if mlist.isMember(user):
            mlist.MailUserPassword(user)
            doc.addError(msg, tag='')
        else:
            # Not a member
            if mlist.private_roster == 0:
                # Public rosters
                doc.addError(_('No such member: %(safeuser)s.'))
            else:
                syslog(
                    'mischief',
                    'Reminder attempt of non-member w/ private rosters: %s',
                    user)
                doc.addError(msg, tag='')
        loginpage(mlist, doc, user, language)
        print(doc.Format())
        return

    # Get the password from the form.
    password = cgidata.getfirst('password', '').strip()
    # Check authentication.  We need to know if the credentials match the user
    # or the site admin, because they are the only ones who are allowed to
    # change things globally.  Specifically, the list admin may not change
    # values globally.
    if mm_cfg.ALLOW_SITE_ADMIN_COOKIES:
        user_or_siteadmin_context = (mm_cfg.AuthUser, mm_cfg.AuthSiteAdmin)
    else:
        # Site and list admins are treated equal so that list admin can pass
        # site admin test. :-(
        user_or_siteadmin_context = (mm_cfg.AuthUser, )
    is_user_or_siteadmin = mlist.WebAuthenticate(user_or_siteadmin_context,
                                                 password, user)
    # Authenticate, possibly using the password supplied in the login page
    if not is_user_or_siteadmin and \
       not mlist.WebAuthenticate((mm_cfg.AuthListAdmin,
                                  mm_cfg.AuthSiteAdmin),
                                 password, user):
        # Not authenticated, so throw up the login page again.  If they tried
        # to authenticate via cgi (instead of cookie), then print an error
        # message.
        if 'password' in cgidata:
            doc.addError(_('Authentication failed.'))
            remote = os.environ.get(
                'HTTP_FORWARDED_FOR',
                os.environ.get(
                    'HTTP_X_FORWARDED_FOR',
                    os.environ.get('REMOTE_ADDR', 'unidentified origin')))
            syslog(
                'security',
                'Authorization failed (private): user=%s: list=%s: remote=%s',
                user, listname, remote)
            # So as not to allow membership leakage, prompt for the email
            # address and the password here.
            if mlist.private_roster != 0:
                syslog('mischief',
                       'Login failure with private rosters: %s from %s', user,
                       remote)
                user = None
            # give an HTTP 401 for authentication failure
            print('Status: 401 Unauthorized')
        loginpage(mlist, doc, user, language)
        print(doc.Format())
        return

    # From here on out, the user is okay to view and modify their membership
    # options.  The first set of checks does not require the list to be
    # locked.

    # However, if a form is submitted for a user who has been asynchronously
    # unsubscribed, uncaught NotAMemberError exceptions can be thrown.

    if not mlist.isMember(user):
        loginpage(mlist, doc, user, language)
        print(doc.Format())
        return

    # Before going further, get the result of CSRF check and do nothing
    # if it has failed.
    if csrf_checked == False:
        doc.addError(
            _('The form lifetime has expired. (request forgery check)'))
        options_page(mlist, doc, user, cpuser, userlang)
        print(doc.Format())
        return

    # See if this is VARHELP on topics.
    varhelp = None
    if 'VARHELP' in cgidata:
        varhelp = cgidata['VARHELP'].value
    elif os.environ.get('QUERY_STRING'):
        # POST methods, even if their actions have a query string, don't get
        # put into FieldStorage's keys :-(
        qs = cgi.parse_qs(os.environ['QUERY_STRING']).get('VARHELP')
        if qs and type(qs) == list:
            varhelp = qs[0]
    if varhelp:
        # Sanitize the topic name.
        varhelp = re.sub('<.*', '', varhelp)
        topic_details(mlist, doc, user, cpuser, userlang, varhelp)
        return

    if 'logout' in cgidata:
        print(mlist.ZapCookie(mm_cfg.AuthUser, user))
        loginpage(mlist, doc, user, language)
        print(doc.Format())
        return

    if 'emailpw' in cgidata:
        mlist.MailUserPassword(user)
        options_page(mlist, doc, user, cpuser, userlang,
                     _('A reminder of your password has been emailed to you.'))
        print(doc.Format())
        return

    if 'othersubs' in cgidata:
        # Only the user or site administrator can view all subscriptions.
        if not is_user_or_siteadmin:
            doc.addError(
                _("""The list administrator may not view the other
            subscriptions for this user."""), _('Note: '))
            options_page(mlist, doc, user, cpuser, userlang)
            print(doc.Format())
            return
        hostname = mlist.host_name
        title = _('List subscriptions for %(safeuser)s on %(hostname)s')
        doc.SetTitle(title)
        doc.AddItem(Header(2, title))
        doc.AddItem(
            _('''Click on a link to visit your options page for the
        requested mailing list.'''))

        # Troll through all the mailing lists that match host_name and see if
        # the user is a member.  If so, add it to the list.
        onlists = []
        for gmlist in lists_of_member(mlist, user) + [mlist]:
            extra = ''
            url = gmlist.GetOptionsURL(user)
            link = Link(url, gmlist.real_name)
            if gmlist.getDeliveryStatus(user) != MemberAdaptor.ENABLED:
                extra += ', ' + _('nomail')
            if user in gmlist.getDigestMemberKeys():
                extra += ', ' + _('digest')
            link = HTMLFormatObject(link, 0) + extra
            onlists.append((gmlist.real_name, link))
        onlists.sort()
        items = OrderedList(*[link for name, link in onlists])
        doc.AddItem(items)
        print(doc.Format())
        return

    if 'change-of-address' in cgidata:
        # We could be changing the user's full name, email address, or both.
        # Watch out for non-ASCII characters in the member's name.
        membername = cgidata.getfirst('fullname')
        # Canonicalize the member's name
        membername = Utils.canonstr(membername, language)
        newaddr = cgidata.getfirst('new-address')
        confirmaddr = cgidata.getfirst('confirm-address')

        oldname = mlist.getMemberName(user)
        set_address = set_membername = 0

        # See if the user wants to change their email address globally.  The
        # list admin is /not/ allowed to make global changes.
        globally = cgidata.getfirst('changeaddr-globally')
        if globally and not is_user_or_siteadmin:
            doc.addError(
                _("""The list administrator may not change the names
            or addresses for this user's other subscriptions.  However, the
            subscription for this mailing list has been changed."""),
                _('Note: '))
            globally = False
        # We will change the member's name under the following conditions:
        # - membername has a value
        # - membername has no value, but they /used/ to have a membername
        if membername and membername != oldname:
            # Setting it to a new value
            set_membername = 1
        if not membername and oldname:
            # Unsetting it
            set_membername = 1
        # We will change the user's address if both newaddr and confirmaddr
        # are non-blank, have the same value, and aren't the currently
        # subscribed email address (when compared case-sensitively).  If both
        # are blank, but membername is set, we ignore it, otherwise we print
        # an error.
        msg = ''
        if newaddr and confirmaddr:
            if newaddr != confirmaddr:
                options_page(mlist, doc, user, cpuser, userlang,
                             _('Addresses did not match!'))
                print(doc.Format())
                return
            if newaddr == cpuser:
                options_page(mlist, doc, user, cpuser, userlang,
                             _('You are already using that email address'))
                print(doc.Format())
                return
            # If they're requesting to subscribe an address which is already a
            # member, and they're /not/ doing it globally, then refuse.
            # Otherwise, we'll agree to do it globally (with a warning
            # message) and let ApprovedChangeMemberAddress() handle already a
            # member issues.
            if mlist.isMember(newaddr):
                safenewaddr = Utils.websafe(newaddr)
                if globally:
                    listname = mlist.real_name
                    msg += _("""\
The new address you requested %(newaddr)s is already a member of the
%(listname)s mailing list, however you have also requested a global change of
address.  Upon confirmation, any other mailing list containing the address
%(safeuser)s will be changed. """)
                    # Don't return
                else:
                    options_page(
                        mlist, doc, user, cpuser, userlang,
                        _('The new address is already a member: %(newaddr)s'))
                    print(doc.Format())
                    return
            set_address = 1
        elif (newaddr or confirmaddr) and not set_membername:
            options_page(mlist, doc, user, cpuser, userlang,
                         _('Addresses may not be blank'))
            print(doc.Format())
            return

        # Standard sigterm handler.
        def sigterm_handler(signum, frame, mlist=mlist):
            mlist.Unlock()
            sys.exit(0)

        signal.signal(signal.SIGTERM, sigterm_handler)
        if set_address:
            if cpuser is None:
                cpuser = user
            # Register the pending change after the list is locked
            msg += _('A confirmation message has been sent to %(newaddr)s. ')
            mlist.Lock()
            try:
                try:
                    mlist.ChangeMemberAddress(cpuser, newaddr, globally)
                    mlist.Save()
                finally:
                    mlist.Unlock()
            except Errors.MMBadEmailError:
                msg = _('Bad email address provided')
            except Errors.MMHostileAddress:
                msg = _('Illegal email address provided')
            except Errors.MMAlreadyAMember:
                msg = _('%(newaddr)s is already a member of the list.')
            except Errors.MembershipIsBanned:
                owneraddr = mlist.GetOwnerEmail()
                msg = _("""%(newaddr)s is banned from this list.  If you
                      think this restriction is erroneous, please contact
                      the list owners at %(owneraddr)s.""")

        if set_membername:
            mlist.Lock()
            try:
                mlist.ChangeMemberName(user, membername, globally)
                mlist.Save()
            finally:
                mlist.Unlock()
            msg += _('Member name successfully changed. ')

        options_page(mlist, doc, user, cpuser, userlang, msg)
        print(doc.Format())
        return

    if 'changepw' in cgidata:
        # Is this list admin and is list admin allowed to change passwords.
        if not (is_user_or_siteadmin
                or mm_cfg.OWNERS_CAN_CHANGE_MEMBER_PASSWORDS):
            doc.addError(
                _("""The list administrator may not change the
                    password for a user."""))
            options_page(mlist, doc, user, cpuser, userlang)
            print(doc.Format())
            return
        newpw = cgidata.getfirst('newpw', '').strip()
        confirmpw = cgidata.getfirst('confpw', '').strip()
        if not newpw or not confirmpw:
            options_page(mlist, doc, user, cpuser, userlang,
                         _('Passwords may not be blank'))
            print(doc.Format())
            return
        if newpw != confirmpw:
            options_page(mlist, doc, user, cpuser, userlang,
                         _('Passwords did not match!'))
            print(doc.Format())
            return

        # See if the user wants to change their passwords globally, however
        # the list admin is /not/ allowed to change passwords globally.
        pw_globally = cgidata.getfirst('pw-globally')
        if pw_globally and not is_user_or_siteadmin:
            doc.addError(
                _("""The list administrator may not change the
            password for this user's other subscriptions.  However, the
            password for this mailing list has been changed."""), _('Note: '))
            pw_globally = False

        mlists = [mlist]

        if pw_globally:
            mlists.extend(lists_of_member(mlist, user))

        for gmlist in mlists:
            change_password(gmlist, user, newpw, confirmpw)

        # Regenerate the cookie so a re-authorization isn't necessary
        print(mlist.MakeCookie(mm_cfg.AuthUser, user))
        options_page(mlist, doc, user, cpuser, userlang,
                     _('Password successfully changed.'))
        print(doc.Format())
        return

    if 'unsub' in cgidata:
        # Was the confirming check box turned on?
        if not cgidata.getfirst('unsubconfirm'):
            options_page(
                mlist, doc, user, cpuser, userlang,
                _('''You must confirm your unsubscription request by turning
                on the checkbox below the <em>Unsubscribe</em> button.  You
                have not been unsubscribed!'''))
            print(doc.Format())
            return

        # Standard signal handler
        def sigterm_handler(signum, frame, mlist=mlist):
            mlist.Unlock()
            sys.exit(0)

        # Okay, zap them.  Leave them sitting at the list's listinfo page.  We
        # must own the list lock, and we want to make sure the user (BAW: and
        # list admin?) is informed of the removal.
        signal.signal(signal.SIGTERM, sigterm_handler)
        mlist.Lock()
        needapproval = False
        try:
            _ = D_
            try:
                mlist.DeleteMember(user,
                                   _('via the member options page'),
                                   userack=1)
            except Errors.MMNeedApproval:
                needapproval = True
            except Errors.NotAMemberError:
                # MAS This except should really be in the outer try so we
                # don't save the list redundantly, but except and finally in
                # the same try requires Python >= 2.5.
                # Setting a switch and making the Save() conditional doesn't
                # seem worth it as the Save() won't change anything.
                pass
            mlist.Save()
        finally:
            _ = i18n._
            mlist.Unlock()
        # Now throw up some results page, with appropriate links.  We can't
        # drop them back into their options page, because that's gone now!
        fqdn_listname = mlist.GetListEmail()
        owneraddr = mlist.GetOwnerEmail()
        url = mlist.GetScriptURL('listinfo', absolute=1)

        title = _('Unsubscription results')
        doc.SetTitle(title)
        doc.AddItem(Header(2, title))
        if needapproval:
            doc.AddItem(
                _("""Your unsubscription request has been received and
            forwarded on to the list moderators for approval.  You will
            receive notification once the list moderators have made their
            decision."""))
        else:
            doc.AddItem(
                _("""You have been successfully unsubscribed from the
            mailing list %(fqdn_listname)s.  If you were receiving digest
            deliveries you may get one more digest.  If you have any questions
            about your unsubscription, please contact the list owners at
            %(owneraddr)s."""))
        doc.AddItem(mlist.GetMailmanFooter())
        print(doc.Format())
        return

    if 'options-submit' in cgidata:
        # Digest action flags
        digestwarn = 0
        cantdigest = 0
        mustdigest = 0

        newvals = []
        # First figure out which options have changed.  The item names come
        # from FormatOptionButton() in HTMLFormatter.py
        for item, flag in (
            ('digest', mm_cfg.Digests),
            ('mime', mm_cfg.DisableMime),
            ('dontreceive', mm_cfg.DontReceiveOwnPosts),
            ('ackposts', mm_cfg.AcknowledgePosts),
            ('disablemail', mm_cfg.DisableDelivery),
            ('conceal', mm_cfg.ConcealSubscription),
            ('remind', mm_cfg.SuppressPasswordReminder),
            ('rcvtopic', mm_cfg.ReceiveNonmatchingTopics),
            ('nodupes', mm_cfg.DontReceiveDuplicates),
        ):
            try:
                newval = int(cgidata.getfirst(item))
            except (TypeError, ValueError):
                newval = None

            # Skip this option if there was a problem or it wasn't changed.
            # Note that delivery status is handled separate from the options
            # flags.
            if newval is None:
                continue
            elif flag == mm_cfg.DisableDelivery:
                status = mlist.getDeliveryStatus(user)
                # Here, newval == 0 means enable, newval == 1 means disable
                if not newval and status != MemberAdaptor.ENABLED:
                    newval = MemberAdaptor.ENABLED
                elif newval and status == MemberAdaptor.ENABLED:
                    newval = MemberAdaptor.BYUSER
                else:
                    continue
            elif newval == mlist.getMemberOption(user, flag):
                continue
            # Should we warn about one more digest?
            if flag == mm_cfg.Digests and \
                   newval == 0 and mlist.getMemberOption(user, flag):
                digestwarn = 1

            newvals.append((flag, newval))

        # The user language is handled a little differently
        if userlang not in mlist.GetAvailableLanguages():
            newvals.append((SETLANGUAGE, mlist.preferred_language))
        else:
            newvals.append((SETLANGUAGE, userlang))

        # Process user selected topics, but don't make the changes to the
        # MailList object; we must do that down below when the list is
        # locked.
        topicnames = cgidata.getvalue('usertopic')
        if topicnames:
            # Some topics were selected.  topicnames can actually be a string
            # or a list of strings depending on whether more than one topic
            # was selected or not.
            if not isinstance(topicnames, list):
                # Assume it was a bare string, so listify it
                topicnames = [topicnames]
            # unquote the topic names
            topicnames = [urllib.parse.unquote_plus(n) for n in topicnames]

        # The standard sigterm handler (see above)
        def sigterm_handler(signum, frame, mlist=mlist):
            mlist.Unlock()
            sys.exit(0)

        # Now, lock the list and perform the changes
        mlist.Lock()
        try:
            signal.signal(signal.SIGTERM, sigterm_handler)
            # `values' is a tuple of flags and the web values
            for flag, newval in newvals:
                # Handle language settings differently
                if flag == SETLANGUAGE:
                    mlist.setMemberLanguage(user, newval)
                # Handle delivery status separately
                elif flag == mm_cfg.DisableDelivery:
                    mlist.setDeliveryStatus(user, newval)
                else:
                    try:
                        mlist.setMemberOption(user, flag, newval)
                    except Errors.CantDigestError:
                        cantdigest = 1
                    except Errors.MustDigestError:
                        mustdigest = 1
            # Set the topics information.
            mlist.setMemberTopics(user, topicnames)
            mlist.Save()
        finally:
            mlist.Unlock()

        # A bag of attributes for the global options
        class Global:
            enable = None
            remind = None
            nodupes = None
            mime = None

            def __bool__(self):
                return len(list(self.__dict__.keys())) > 0

        globalopts = Global()

        # The enable/disable option and the password remind option may have
        # their global flags sets.
        if cgidata.getfirst('deliver-globally'):
            # Yes, this is inefficient, but the list is so small it shouldn't
            # make much of a difference.
            for flag, newval in newvals:
                if flag == mm_cfg.DisableDelivery:
                    globalopts.enable = newval
                    break

        if cgidata.getfirst('remind-globally'):
            for flag, newval in newvals:
                if flag == mm_cfg.SuppressPasswordReminder:
                    globalopts.remind = newval
                    break

        if cgidata.getfirst('nodupes-globally'):
            for flag, newval in newvals:
                if flag == mm_cfg.DontReceiveDuplicates:
                    globalopts.nodupes = newval
                    break

        if cgidata.getfirst('mime-globally'):
            for flag, newval in newvals:
                if flag == mm_cfg.DisableMime:
                    globalopts.mime = newval
                    break

        # Change options globally, but only if this is the user or site admin,
        # /not/ if this is the list admin.
        if globalopts:
            if not is_user_or_siteadmin:
                doc.addError(
                    _("""The list administrator may not change the
                options for this user's other subscriptions.  However the
                options for this mailing list subscription has been
                changed."""), _('Note: '))
            else:
                for gmlist in lists_of_member(mlist, user):
                    global_options(gmlist, user, globalopts)

        # Now print the results
        if cantdigest:
            msg = _('''The list administrator has disabled digest delivery for
            this list, so your delivery option has not been set.  However your
            other options have been set successfully.''')
        elif mustdigest:
            msg = _('''The list administrator has disabled non-digest delivery
            for this list, so your delivery option has not been set.  However
            your other options have been set successfully.''')
        else:
            msg = _('You have successfully set your options.')

        if digestwarn:
            msg += _('You may get one last digest.')

        options_page(mlist, doc, user, cpuser, userlang, msg)
        print(doc.Format())
        return

    if mlist.isMember(user):
        options_page(mlist, doc, user, cpuser, userlang)
    else:
        loginpage(mlist, doc, user, userlang)
    print(doc.Format())
示例#5
0
def UpdateOldVars(l, stored_state):
    """Transform old variable values into new ones, deleting old ones.
    stored_state is last snapshot from file, as opposed to from InitVars()."""

    def PreferStored(oldname, newname, newdefault=uniqueval,
                     l=l, state=stored_state):
        """Use specified old value if new value is not in stored state.

        If the old attr does not exist, and no newdefault is specified, the
        new attr is *not* created - so either specify a default or be positive
        that the old attr exists - or don't depend on the new attr.

        """
        if hasattr(l, oldname):
            if not state.has_key(newname):
                setattr(l, newname, getattr(l, oldname))
            delattr(l, oldname)
        if not hasattr(l, newname) and newdefault is not uniqueval:
                setattr(l, newname, newdefault)

    # Migrate to 2.1b3, baw 17-Aug-2001
    if hasattr(l, 'dont_respond_to_post_requests'):
        oldval = getattr(l, 'dont_respond_to_post_requests')
        if not hasattr(l, 'respond_to_post_requests'):
            l.respond_to_post_requests = not oldval
        del l.dont_respond_to_post_requests

    # Migrate to 2.1b3, baw 13-Oct-2001
    # Basic defaults for new variables
    if not hasattr(l, 'default_member_moderation'):
        l.default_member_moderation = mm_cfg.DEFAULT_DEFAULT_MEMBER_MODERATION
    if not hasattr(l, 'accept_these_nonmembers'):
        l.accept_these_nonmembers = []
    if not hasattr(l, 'hold_these_nonmembers'):
        l.hold_these_nonmembers = []
    if not hasattr(l, 'reject_these_nonmembers'):
        l.reject_these_nonmembers = []
    if not hasattr(l, 'discard_these_nonmembers'):
        l.discard_these_nonmembers = []
    if not hasattr(l, 'forward_auto_discards'):
        l.forward_auto_discards = mm_cfg.DEFAULT_FORWARD_AUTO_DISCARDS
    if not hasattr(l, 'generic_nonmember_action'):
        l.generic_nonmember_action = mm_cfg.DEFAULT_GENERIC_NONMEMBER_ACTION
    # Now convert what we can...  Note that the interaction between the
    # MM2.0.x attributes `moderated', `member_posting_only', and `posters' is
    # so confusing, it makes my brain really ache.  Which is why they go away
    # in MM2.1.  I think the best we can do semantically is the following:
    #
    # - If moderated == yes, then any sender who's address is not on the
    #   posters attribute would get held for approval.  If the sender was on
    #   the posters list, then we'd defer judgement to a later step
    # - If member_posting_only == yes, then members could post without holds,
    #   and if there were any addresses added to posters, they could also post
    #   without holds.
    # - If member_posting_only == no, then what happens depends on the value
    #   of the posters attribute:
    #       o If posters was empty, then anybody can post without their
    #         message being held for approval
    #       o If posters was non-empty, then /only/ those addresses could post
    #         without approval, i.e. members not on posters would have their
    #         messages held for approval.
    #
    # How to translate this mess to MM2.1 values?  I'm sure I got this wrong
    # before, but here's how we're going to do it, as of MM2.1b3.
    #
    # - We'll control member moderation through their Moderate flag, and
    #   non-member moderation through the generic_nonmember_action,
    #   hold_these_nonmembers, and accept_these_nonmembers.
    # - If moderated == yes then we need to troll through the addresses on
    #   posters, and any non-members would get added to
    #   accept_these_nonmembers.  /Then/ we need to troll through the
    #   membership and any member on posters would get their Moderate flag
    #   unset, while members not on posters would get their Moderate flag set.
    #   Then generic_nonmember_action gets set to 1 (hold) so nonmembers get
    #   moderated, and default_member_moderation will be set to 1 (hold) so
    #   new members will also get held for moderation.  We'll stop here.
    # - We only get to here if moderated == no.
    # - If member_posting_only == yes, then we'll turn off the Moderate flag
    #   for members.  We troll through the posters attribute and add all those
    #   addresses to accept_these_nonmembers.  We'll also set
    #   generic_nonmember_action to 1 and default_member_moderation to 0.
    #   We'll stop here.
    # - We only get to here if member_posting_only == no
    # - If posters is empty, then anybody could post without being held for
    #   approval, so we'll set generic_nonmember_action to 0 (accept), and
    #   we'll turn off the Moderate flag for all members.  We'll also turn off
    #   default_member_moderation so new members can post without approval.
    #   We'll stop here.
    # - We only get here if posters is non-empty.
    # - This means that /only/ the addresses on posters got to post without
    #   being held for approval.  So first, we troll through posters and add
    #   all non-members to accept_these_nonmembers.  Then we troll through the
    #   membership and if their address is on posters, we'll clear their
    #   Moderate flag, otherwise we'll set it.  We'll turn on
    #   default_member_moderation so new members get moderated.  We'll set
    #   generic_nonmember_action to 1 (hold) so all other non-members will get
    #   moderated.  And I think we're finally done.
    #
    # SIGH.
    if hasattr(l, 'moderated'):
        # We'll assume we're converting all these attributes at once
        if l.moderated:
            #syslog('debug', 'Case 1')
            for addr in l.posters:
                if not l.isMember(addr):
                    l.accept_these_nonmembers.append(addr)
            for member in l.getMembers():
                l.setMemberOption(member, mm_cfg.Moderate,
                                  # reset for explicitly named members
                                  member not in l.posters)
            l.generic_nonmember_action = 1
            l.default_member_moderation = 1
        elif l.member_posting_only:
            #syslog('debug', 'Case 2')
            for addr in l.posters:
                if not l.isMember(addr):
                    l.accept_these_nonmembers.append(addr)
            for member in l.getMembers():
                l.setMemberOption(member, mm_cfg.Moderate, 0)
            l.generic_nonmember_action = 1
            l.default_member_moderation = 0
        elif not l.posters:
            #syslog('debug', 'Case 3')
            for member in l.getMembers():
                l.setMemberOption(member, mm_cfg.Moderate, 0)
            l.generic_nonmember_action = 0
            l.default_member_moderation = 0
        else:
            #syslog('debug', 'Case 4')
            for addr in l.posters:
                if not l.isMember(addr):
                    l.accept_these_nonmembers.append(addr)
            for member in l.getMembers():
                l.setMemberOption(member, mm_cfg.Moderate,
                                  # reset for explicitly named members
                                  member not in l.posters)
            l.generic_nonmember_action = 1
            l.default_member_moderation = 1
        # Now get rid of the old attributes
        del l.moderated
        del l.posters
        del l.member_posting_only
    if hasattr(l, 'forbidden_posters'):
        # For each of the posters on this list, if they are members, toggle on
        # their moderation flag.  If they are not members, then add them to
        # hold_these_nonmembers.
        forbiddens = l.forbidden_posters
        for addr in forbiddens:
            if l.isMember(addr):
                l.setMemberOption(addr, mm_cfg.Moderate, 1)
            else:
                l.hold_these_nonmembers.append(addr)
        del l.forbidden_posters

    # Migrate to 1.0b6, klm 10/22/1998:
    PreferStored('reminders_to_admins', 'umbrella_list',
                 mm_cfg.DEFAULT_UMBRELLA_LIST)

    # Migrate up to 1.0b5:
    PreferStored('auto_subscribe', 'open_subscribe')
    PreferStored('closed', 'private_roster')
    PreferStored('mimimum_post_count_before_removal',
                 'mimimum_post_count_before_bounce_action')
    PreferStored('bad_posters', 'forbidden_posters')
    PreferStored('automatically_remove', 'automatic_bounce_action')
    if hasattr(l, "open_subscribe"):
        if l.open_subscribe:
            if mm_cfg.ALLOW_OPEN_SUBSCRIBE:
                l.subscribe_policy = 0
            else:
                l.subscribe_policy = 1
        else:
            l.subscribe_policy = 2      # admin approval
        delattr(l, "open_subscribe")
    if not hasattr(l, "administrivia"):
        setattr(l, "administrivia", mm_cfg.DEFAULT_ADMINISTRIVIA)
    if not hasattr(l, "admin_member_chunksize"):
        setattr(l, "admin_member_chunksize",
                mm_cfg.DEFAULT_ADMIN_MEMBER_CHUNKSIZE)
    #
    # this attribute was added then deleted, so there are a number of
    # cases to take care of
    #
    if hasattr(l, "posters_includes_members"):
        if l.posters_includes_members:
            if l.posters:
                l.member_posting_only = 1
        else:
            if l.posters:
                l.member_posting_only = 0
        delattr(l, "posters_includes_members")
    elif l.data_version <= 10 and l.posters:
        # make sure everyone gets the behavior the list used to have, but only
        # for really old versions of Mailman (1.0b5 or before).  Any newer
        # version of Mailman should not get this attribute whacked.
        l.member_posting_only = 0
    #
    # transfer the list data type for holding members and digest members
    # to the dict data type starting file format version 11
    #
    if type(l.members) is ListType:
        members = {}
        for m in l.members:
            members[m] = 1
        l.members = members
    if type(l.digest_members) is ListType:
        dmembers = {}
        for dm in l.digest_members:
            dmembers[dm] = 1
        l.digest_members = dmembers
    #
    # set admin_notify_mchanges
    #
    if not hasattr(l, "admin_notify_mchanges"):
        setattr(l, "admin_notify_mchanges",
                mm_cfg.DEFAULT_ADMIN_NOTIFY_MCHANGES)
    #
    # Convert the members and digest_members addresses so that the keys of
    # both these are always lowercased, but if there is a case difference, the
    # value contains the case preserved value
    #
    for k in l.members.keys():
        if k.lower() <> k:
            l.members[k.lower()] = Utils.LCDomain(k)
            del l.members[k]
        elif type(l.members[k]) == StringType and k == l.members[k].lower():
            # already converted
            pass
        else:
            l.members[k] = 0
    for k in l.digest_members.keys():
        if k.lower() <> k:
            l.digest_members[k.lower()] = Utils.LCDomain(k)
            del l.digest_members[k]
        elif type(l.digest_members[k]) == StringType and \
                 k == l.digest_members[k].lower():
            # already converted
            pass
        else:
            l.digest_members[k] = 0
示例#6
0
    if lenparts < 2:
        user = cgidata.getvalue('email')
        if not user:
            # If we're coming from the listinfo page and we left the email
            # address field blank, it's not an error.  Likewise if we're
            # coming from anywhere else. Only issue the error if we came
            # via one of our buttons.
            if (cgidata.getvalue('login') or cgidata.getvalue('login-unsub')
                    or cgidata.getvalue('login-remind')):
                doc.addError(_('No address given'))
            loginpage(mlist, doc, None, language)
            print doc.Format()
            return
    else:
        user = Utils.LCDomain(Utils.UnobscureEmail(SLASH.join(parts[1:])))

    # Avoid cross-site scripting attacks
    safeuser = Utils.websafe(user)
    try:
        Utils.ValidateEmail(user)
    except Errors.EmailAddressError:
        doc.addError(_('Illegal Email Address: %(safeuser)s'))
        loginpage(mlist, doc, None, language)
        print doc.Format()
        return
    # Sanity check the user, but only give the "no such member" error when
    # using public rosters, otherwise, we'll leak membership information.
    if not mlist.isMember(user) and mlist.private_roster == 0:
        doc.addError(_('No such member: %(safeuser)s.'))
        loginpage(mlist, doc, None, language)