Пример #1
0
def loginpage(mlist, scriptname, msg='', frontpage=None):
    url = mlist.GetScriptURL(scriptname)
    if frontpage:
        actionurl = url
    else:
        actionurl = Utils.GetRequestURI(url)
    if msg:
        msg = FontAttr(msg, color='#ff0000', size='+1').Format()
        # give an HTTP 401 for authentication failure
        print 'Status: 401 Unauthorized'
    if scriptname == 'admindb':
        who = _('Moderator')
    else:
        who = _('Administrator')
    # Language stuff
    charset = Utils.GetCharSet(mlist.preferred_language)
    print 'Content-type: text/html; charset=' + charset + '\n\n'
    print Utils.maketext('admlogin.html', {
        'listname': mlist.real_name,
        'path': actionurl,
        'message': msg,
        'who': who,
    },
                         mlist=mlist)
    print mlist.GetMailmanFooter()
Пример #2
0
 def __sendAdminBounceNotice(self, member, msg, did=_('disabled')):
     # BAW: This is a bit kludgey, but we're not providing as much
     # information in the new admin bounce notices as we used to (some of
     # it was of dubious value).  However, we'll provide empty, strange, or
     # meaningless strings for the unused %()s fields so that the language
     # translators don't have to provide new templates.
     siteowner = Utils.get_site_email(self.host_name)
     text = Utils.maketext('bounce.txt', {
         'listname': self.real_name,
         'addr': member,
         'negative': '',
         'did': did,
         'but': '',
         'reenable': '',
         'owneraddr': siteowner,
     },
                           mlist=self)
     subject = _('Bounce action notification')
     umsg = Message.UserNotification(self.GetOwnerEmail(),
                                     siteowner,
                                     subject,
                                     lang=self.preferred_language)
     # BAW: Be sure you set the type before trying to attach, or you'll get
     # a MultipartConversionError.
     umsg.set_type('multipart/mixed')
     umsg.attach(
         MIMEText(text, _charset=Utils.GetCharSet(self.preferred_language)))
     if isinstance(msg, StringType):
         umsg.attach(MIMEText(msg))
     else:
         umsg.attach(MIMEMessage(msg))
     umsg.send(self)
Пример #3
0
def process(mlist, msg, msgdata):
    # Extract the sender's address and find them in the user database
    sender = msgdata.get('original_sender', msg.get_sender())
    try:
        ack = mlist.getMemberOption(sender, mm_cfg.AcknowledgePosts)
        if not ack:
            return
    except Errors.NotAMemberError:
        return
    # Okay, they want acknowledgement of their post.  Give them their original
    # subject.  BAW: do we want to use the decoded header?
    origsubj = msgdata.get('origsubj', msg.get('subject', _('(no subject)')))
    # Get the user's preferred language
    lang = msgdata.get('lang', mlist.getMemberLanguage(sender))
    # Now get the acknowledgement template
    realname = mlist.real_name
    text = Utils.maketext(
        'postack.txt', {
            'subject': Utils.oneline(origsubj, Utils.GetCharSet(lang)),
            'listname': realname,
            'listinfo_url': mlist.GetScriptURL('listinfo', absolute=1),
            'optionsurl': mlist.GetOptionsURL(sender, absolute=1),
        },
        lang=lang,
        mlist=mlist,
        raw=1)
    # Craft the outgoing message, with all headers and attributes
    # necessary for general delivery.  Then enqueue it to the outgoing
    # queue.
    subject = _('%(realname)s post acknowledgement')
    usermsg = Message.UserNotification(sender, mlist.GetBouncesEmail(),
                                       subject, text, lang)
    usermsg.send(mlist)
Пример #4
0
    def decode_headers(self):
        """MIME-decode headers.

        If the email, subject, or author attributes contain non-ASCII
        characters using the encoded-word syntax of RFC 2047, decoded versions
        of those attributes are placed in the self.decoded (a dictionary).

        If the list's charset differs from the header charset, an attempt is
        made to decode the headers as Unicode.  If that fails, they are left
        undecoded.
        """
        author = self.decode_charset(self.author)
        subject = self.decode_charset(self.subject)
        if author:
            self.decoded['author'] = author
            email = self.decode_charset(self.email)
            if email:
                self.decoded['email'] = email
        if subject:
            if mm_cfg.ARCHIVER_OBSCURES_EMAILADDRS:
                otrans = i18n.get_translation()
                try:
                    i18n.set_language(self._lang)
                    atmark = unicode(_(' at '), Utils.GetCharSet(self._lang))
                    subject = re.sub(r'([-+,.\w]+)@([-+.\w]+)',
                                     '\g<1>' + atmark + '\g<2>', subject)
                finally:
                    i18n.set_translation(otrans)
            self.decoded['subject'] = subject
        self.decoded['stripped'] = self.strip_subject(subject or self.subject)
Пример #5
0
    def Format(self, indent=0, **kws):
        charset = 'us-ascii'
        if self.language and Utils.IsLanguage(self.language):
            charset = Utils.GetCharSet(self.language)
        output = ['Content-Type: text/html; charset=%s' % charset]
        output.append('Cache-control: no-cache\n')
        if not self.suppress_head:
            kws.setdefault('bgcolor', self.bgcolor)
            tab = ' ' * indent
            output.extend([tab, '<HTML>', '<HEAD>'])
            if mm_cfg.IMAGE_LOGOS:
                output.append('<LINK REL="SHORTCUT ICON" HREF="%s">' %
                              (mm_cfg.IMAGE_LOGOS + mm_cfg.SHORTCUT_ICON))
            # Hit all the bases
            output.append('<META http-equiv="Content-Type" '
                          'content="text/html; charset=%s">' % charset)
            if self.title:
                output.append('%s<TITLE>%s</TITLE>' % (tab, self.title))
            # Add CSS to visually hide some labeling text but allow screen
            # readers to read it.
            output.append("""\
<style type="text/css">
    div.hidden
        {position:absolute;
        left:-10000px;
        top:auto;
        width:1px;
        height:1px;
        overflow:hidden;}
</style>
""")
            if mm_cfg.WEB_HEAD_ADD:
                output.append(mm_cfg.WEB_HEAD_ADD)
            output.append('%s</HEAD>' % tab)
            quals = []
            # Default link colors
            if mm_cfg.WEB_VLINK_COLOR:
                kws.setdefault('vlink', mm_cfg.WEB_VLINK_COLOR)
            if mm_cfg.WEB_ALINK_COLOR:
                kws.setdefault('alink', mm_cfg.WEB_ALINK_COLOR)
            if mm_cfg.WEB_LINK_COLOR:
                kws.setdefault('link', mm_cfg.WEB_LINK_COLOR)
            for k, v in kws.items():
                quals.append('%s="%s"' % (k, v))
            output.append('%s<BODY %s' % (tab, SPACE.join(quals)))
            # Language direction
            direction = Utils.GetDirection(self.language)
            output.append('dir="%s">' % direction)
        # Always do this...
        output.append(Container.Format(self, indent))
        if not self.suppress_head:
            output.append('%s</BODY>' % tab)
            output.append('%s</HTML>' % tab)
        return NL.join(output)
Пример #6
0
 def GetStandardReplacements(self, lang=None):
     dmember_len = len(self.getDigestMemberKeys())
     member_len = len(self.getRegularMemberKeys())
     # If only one language is enabled for this mailing list, omit the
     # language choice buttons.
     if len(self.GetAvailableLanguages()) == 1:
         listlangs = _(Utils.GetLanguageDescr(self.preferred_language))
     else:
         listlangs = self.GetLangSelectBox(lang).Format()
     if lang:
         cset = Utils.GetCharSet(lang) or 'us-ascii'
     else:
         cset = Utils.GetCharSet(self.preferred_language) or 'us-ascii'
     d = {
         '<mm-mailman-footer>' : self.GetMailmanFooter(),
         '<mm-list-name>' : self.real_name,
         '<mm-email-user>' : self._internal_name,
         '<mm-list-description>' :
             Utils.websafe(self.GetDescription(cset)),
         '<mm-list-info>' :
             '<!---->' + BR.join(self.info.split(NL)) + '<!---->',
         '<mm-form-end>'  : self.FormatFormEnd(),
         '<mm-archive>'   : self.FormatArchiveAnchor(),
         '</mm-archive>'  : '</a>',
         '<mm-list-subscription-msg>' : self.FormatSubscriptionMsg(),
         '<mm-restricted-list-message>' : \
             self.RestrictedListMessage(_('The current archive'),
                                        self.archive_private),
         '<mm-num-reg-users>' : repr(member_len),
         '<mm-num-digesters>' : repr(dmember_len),
         '<mm-num-members>' : (repr(member_len + dmember_len)),
         '<mm-posting-addr>' : '%s' % self.GetListEmail(),
         '<mm-request-addr>' : '%s' % self.GetRequestEmail(),
         '<mm-owner>' : self.GetOwnerEmail(),
         '<mm-reminder>' : self.FormatReminder(self.preferred_language),
         '<mm-host>' : self.host_name,
         '<mm-list-langs>' : listlangs,
         }
     if mm_cfg.IMAGE_LOGOS:
         d['<mm-favicon>'] = mm_cfg.IMAGE_LOGOS + mm_cfg.SHORTCUT_ICON
     return d
Пример #7
0
 def decode_charset(self, field):
     # TK: This function was rewritten for unifying to Unicode.
     # Convert 'field' into Unicode one line string.
     try:
         pairs = decode_header(field)
         ustr = make_header(pairs).__unicode__()
     except (LookupError, UnicodeError, ValueError, HeaderParseError):
         # assume list's language
         cset = Utils.GetCharSet(self._mlist.preferred_language)
         if cset == 'us-ascii':
             cset = 'iso-8859-1'  # assume this for English list
         ustr = unicode(field, cset, 'replace')
     return u''.join(ustr.splitlines())
Пример #8
0
def uheader(mlist, s, header_name=None, continuation_ws='\t', maxlinelen=None):
    # Get the charset to encode the string in. Then search if there is any
    # non-ascii character is in the string. If there is and the charset is
    # us-ascii then we use iso-8859-1 instead. If the string is ascii only
    # we use 'us-ascii' if another charset is specified.
    charset = Utils.GetCharSet(mlist.preferred_language)
    if nonascii.search(s):
        # use list charset but ...
        if charset == 'us-ascii':
            charset = 'iso-8859-1'
    else:
        # there is no nonascii so ...
        charset = 'us-ascii'
    return Header(s, charset, maxlinelen, header_name, continuation_ws)
Пример #9
0
def uheader(mlist, s, header_name=None, continuation_ws=' ', maxlinelen=None):
    # Get the charset to encode the string in. Then search if there is any
    # non-ascii character is in the string. If there is and the charset is
    # us-ascii then we use iso-8859-1 instead. If the string is ascii only
    # we use 'us-ascii' if another charset is specified.
    charset = Utils.GetCharSet(mlist.preferred_language)
    if charset == 'us-ascii':
        charset = 'iso-8859-1'
    try:
        return Header(s, charset, maxlinelen, header_name, continuation_ws)
    except UnicodeError:
        syslog('error', 'list: %s: can\'t decode "%s" as %s',
               mlist.internal_name(), s, charset)
        return Header('', charset, maxlinelen, header_name, continuation_ws)
Пример #10
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)
Пример #11
0
 def BounceMessage(self, msg, msgdata, e=None):
     # Bounce a message back to the sender, with an error message if
     # provided in the exception argument.
     sender = msg.get_sender()
     subject = msg.get('subject', _('(no subject)'))
     subject = Utils.oneline(subject,
                             Utils.GetCharSet(self.preferred_language))
     if e is None:
         notice = _('[No bounce details are available]')
     else:
         notice = _(e.notice())
     # Currently we always craft bounces as MIME messages.
     bmsg = Message.UserNotification(msg.get_sender(),
                                     self.GetOwnerEmail(),
                                     subject,
                                     lang=self.preferred_language)
     # BAW: Be sure you set the type before trying to attach, or you'll get
     # a MultipartConversionError.
     bmsg.set_type('multipart/mixed')
     txt = MIMEText(notice,
                    _charset=Utils.GetCharSet(self.preferred_language))
     bmsg.attach(txt)
     bmsg.attach(MIMEMessage(msg))
     bmsg.send(self)
Пример #12
0
 def ForwardMessage(self, msg, text=None, subject=None, tomoderators=True):
     # Wrap the message as an attachment
     if text is None:
         text = _('No reason given')
     if subject is None:
         text = _('(no subject)')
     text = MIMEText(Utils.wrap(text),
                     _charset=Utils.GetCharSet(self.preferred_language))
     attachment = MIMEMessage(msg)
     notice = Message.OwnerNotification(
         self, subject, tomoderators=tomoderators)
     # Make it look like the message is going to the -owner address
     notice.set_type('multipart/mixed')
     notice.attach(text)
     notice.attach(attachment)
     notice.send(self)
def process(mlist, msg, msgdata):
    # This is the negation of we're wrapping because dmarc_moderation_action
    # is wrap this message or from_is_list applies and is wrap.
    if not (msgdata.get('from_is_list') == 2 or
            (mlist.from_is_list == 2 and msgdata.get('from_is_list') == 0)):
        # Now see if we need to add a From:, Reply-To: or Cc: without wrapping.
        # See comments in CookHeaders.change_header for why we do this here.
        a_h = msgdata.get('add_header')
        if a_h:
            if a_h.get('From'):
                del msg['from']
                msg['From'] = a_h.get('From')
            if a_h.get('Reply-To'):
                del msg['reply-to']
                msg['Reply-To'] = a_h.get('Reply-To')
            if a_h.get('Cc'):
                del msg['cc']
                msg['Cc'] = a_h.get('Cc')
        return

    # There are various headers in msg that we don't want, so we basically
    # make a copy of the msg, then delete almost everything and set/copy
    # what we want.
    omsg = copy.deepcopy(msg)
    for key in list(msg.keys()):
        if key.lower() not in KEEPERS:
            del msg[key]
    msg['MIME-Version'] = '1.0'
    msg['Message-ID'] = Utils.unique_message_id(mlist)
    # Add the headers from CookHeaders.
    for k, v in list(msgdata['add_header'].items()):
        msg[k] = v
    # Are we including dmarc_wrapped_message_text?  I.e., do we have text and
    # are we wrapping because of dmarc_moderation_action?
    if mlist.dmarc_wrapped_message_text and msgdata.get('from_is_list') == 2:
        part1 = text.MIMEText(Utils.wrap(mlist.dmarc_wrapped_message_text),
                              'plain',
                              Utils.GetCharSet(mlist.preferred_language))
        part1['Content-Disposition'] = 'inline'
        part2 = message.MIMEMessage(omsg)
        part2['Content-Disposition'] = 'inline'
        msg['Content-Type'] = 'multipart/mixed'
        msg.set_payload([part1, part2])
    else:
        msg['Content-Type'] = 'message/rfc822'
        msg['Content-Disposition'] = 'inline'
        msg.set_payload([omsg])
Пример #14
0
    def html_TOC(self):
        mlist = self.maillist
        listname = mlist.internal_name()
        mbox = os.path.join(mlist.archive_dir() + '.mbox', listname + '.mbox')
        d = {
            "listname": mlist.real_name,
            "listinfo": mlist.GetScriptURL('listinfo', absolute=1),
            "fullarch": '../%s.mbox/%s.mbox' % (listname, listname),
            "size": sizeof(mbox, mlist.preferred_language),
            'meta': '',
        }
        # Avoid i18n side-effects
        otrans = i18n.get_translation()
        i18n.set_language(mlist.preferred_language)
        try:
            if not self.archives:
                d["noarchive_msg"] = _(
                    '<P>Currently, there are no archives. </P>')
                d["archive_listing_start"] = ""
                d["archive_listing_end"] = ""
                d["archive_listing"] = ""
            else:
                d["noarchive_msg"] = ""
                d["archive_listing_start"] = quick_maketext(
                    'archliststart.html',
                    lang=mlist.preferred_language,
                    mlist=mlist)
                d["archive_listing_end"] = quick_maketext('archlistend.html',
                                                          mlist=mlist)

                accum = []
                for a in self.archives:
                    accum.append(self.html_TOC_entry(a))
                d["archive_listing"] = EMPTYSTRING.join(accum)
        finally:
            i18n.set_translation(otrans)
        # The TOC is always in the charset of the list's preferred language
        d['meta'] += html_charset % Utils.GetCharSet(mlist.preferred_language)
        # The site can disable public access to the mbox file.
        if mm_cfg.PUBLIC_MBOX:
            template = 'archtoc.html'
        else:
            template = 'archtocnombox.html'
        return quick_maketext(template, d, mlist=mlist)
Пример #15
0
 def ParseTags(self, template, replacements, lang=None):
     if lang is None:
         charset = 'us-ascii'
     else:
         charset = Utils.GetCharSet(lang)
     text = Utils.maketext(template, raw=1, lang=lang, mlist=self)
     parts = re.split('(</?[Mm][Mm]-[^>]*>)', text)
     i = 1
     while i < len(parts):
         tag = parts[i].lower()
         if replacements.has_key(tag):
             repl = replacements[tag]
             if isinstance(repl, type(u'')):
                 repl = repl.encode(charset, 'replace')
             parts[i] = repl
         else:
             parts[i] = ''
         i = i + 2
     return EMPTYSTRING.join(parts)
Пример #16
0
    def as_html(self):
        d = self.__dict__.copy()
        # avoid i18n side-effects
        otrans = i18n.get_translation()
        i18n.set_language(self._lang)
        try:
            d["prev"], d["prev_wsubj"] = self._get_prev()
            d["next"], d["next_wsubj"] = self._get_next()

            d["email_html"] = self.quote(self.email)
            d["title"] = self.quote(self.subject)
            d["subject_html"] = self.quote(self.subject)
            d["message_id"] = self.quote(self._message_id)
            # TK: These two _url variables are used to compose a response
            # from the archive web page.  So, ...
            d["subject_url"] = url_quote('Re: ' + self.subject)
            d["in_reply_to_url"] = url_quote(self._message_id)
            if mm_cfg.ARCHIVER_OBSCURES_EMAILADDRS:
                # Point the mailto url back to the list
                author = re.sub('@', _(' at '), self.author)
                emailurl = self._mlist.GetListEmail()
            else:
                author = self.author
                emailurl = self.email
            d["author_html"] = self.quote(author)
            d["email_url"] = url_quote(emailurl)
            d["datestr_html"] = self.quote(i18n.ctime(int(self.date)))
            d["body"] = self._get_body()
            d['listurl'] = self._mlist.GetScriptURL('listinfo', absolute=1)
            d['listname'] = self._mlist.real_name
            d['encoding'] = ''
        finally:
            i18n.set_translation(otrans)

        charset = Utils.GetCharSet(self._lang)
        d["encoding"] = html_charset % charset

        self._add_decoded(d)
        return quick_maketext('article.html',
                              d,
                              lang=self._lang,
                              mlist=self._mlist)
Пример #17
0
 def sendProbe(self, member, msg):
     listname = self.real_name
     # Put together the substitution dictionary.
     d = {
         'listname': listname,
         'address': member,
         'optionsurl': self.GetOptionsURL(member, absolute=True),
         'owneraddr': self.GetOwnerEmail(),
     }
     text = Utils.maketext('probe.txt',
                           d,
                           lang=self.getMemberLanguage(member),
                           mlist=self)
     # Calculate the VERP'd sender address for bounce processing of the
     # probe message.
     token = self.pend_new(Pending.PROBE_BOUNCE, member, msg)
     probedict = {
         'bounces': self.internal_name() + '-bounces',
         'token': token,
     }
     probeaddr = '%s@%s' % (
         (mm_cfg.VERP_PROBE_FORMAT % probedict), self.host_name)
     # Calculate the Subject header, in the member's preferred language
     ulang = self.getMemberLanguage(member)
     otrans = i18n.get_translation()
     i18n.set_language(ulang)
     try:
         subject = _('%(listname)s mailing list probe message')
     finally:
         i18n.set_translation(otrans)
     outer = Message.UserNotification(member,
                                      probeaddr,
                                      subject,
                                      lang=ulang)
     outer.set_type('multipart/mixed')
     text = MIMEText(text, _charset=Utils.GetCharSet(ulang))
     outer.attach(text)
     outer.attach(MIMEMessage(msg))
     # Turn off further VERP'ing in the final delivery step.  We set
     # probe_token for the OutgoingRunner to more easily handling local
     # rejects of probe messages.
     outer.send(self, envsender=probeaddr, verp=False, probe_token=token)
Пример #18
0
 def __init__(self, recip, sender, subject=None, text=None, lang=None):
     Message.__init__(self)
     charset = None
     if lang is not None:
         charset = Charset(Utils.GetCharSet(lang))
     if text is not None:
         self.set_payload(text, charset)
     if subject is None:
         subject = '(no subject)'
     self['Subject'] = Header(subject,
                              charset,
                              header_name='Subject',
                              errors='replace')
     self['From'] = sender
     if isinstance(recip, list):
         self['To'] = COMMASPACE.join(recip)
         self.recips = recip
     else:
         self['To'] = recip
         self.recips = [recip]
Пример #19
0
def do_discard(mlist, msg):
    sender = msg.get_sender()
    # Do we forward auto-discards to the list owners?
    if mlist.forward_auto_discards:
        lang = mlist.preferred_language
        varhelp = '%s/?VARHELP=privacy/sender/discard_these_nonmembers' % \
                  mlist.GetScriptURL('admin', absolute=1)
        nmsg = Message.UserNotification(mlist.GetOwnerEmail(),
                                        mlist.GetBouncesEmail(),
                                        _('Auto-discard notification'),
                                        lang=lang)
        nmsg.set_type('multipart/mixed')
        text = MIMEText(Utils.wrap(
            _('The attached message has been automatically discarded.')),
                        _charset=Utils.GetCharSet(lang))
        nmsg.attach(text)
        nmsg.attach(MIMEMessage(msg))
        nmsg.send(mlist)
    # Discard this sucker
    raise Errors.DiscardMessage
Пример #20
0
def quick_maketext(templatefile, dict=None, lang=None, mlist=None):
    if mlist is None:
        listname = ''
    else:
        listname = mlist._internal_name
    if lang is None:
        if mlist is None:
            lang = mm_cfg.DEFAULT_SERVER_LANGUAGE
        else:
            lang = mlist.preferred_language
    cachekey = (templatefile, lang, listname)
    filepath =  _templatefilepathcache.get(cachekey)
    if filepath:
        template = _templatecache.get(filepath)
    if filepath is None or template is None:
        # Use the basic maketext, with defaults to get the raw template
        template, filepath = Utils.findtext(templatefile, lang=lang,
                                            raw=True, mlist=mlist)
        _templatefilepathcache[cachekey] = filepath
        _templatecache[filepath] = template
    # Copied from Utils.maketext()
    text = template
    if dict is not None:
        try:
            sdict = SafeDict(dict)
            try:
                text = sdict.interpolate(template)
            except UnicodeError:
                # Try again after coercing the template to unicode
                utemplate = unicode(template,
                                    Utils.GetCharSet(lang),
                                    'replace')
                text = sdict.interpolate(utemplate)
        except (TypeError, ValueError):
            # The template is really screwed up
            pass
    # Make sure the text is in the given character set, or html-ify any bogus
    # characters.
    return Utils.uncanonstr(text, lang)
Пример #21
0
 def Format(self, indent=0, **kws):
     charset = 'us-ascii'
     if self.language and Utils.IsLanguage(self.language):
         charset = Utils.GetCharSet(self.language)
     output = ['Content-Type: text/html; charset=%s' % charset]
     output.append('Cache-control: no-cache\n')
     if not self.suppress_head:
         kws.setdefault('bgcolor', self.bgcolor)
         tab = ' ' * indent
         output.extend([tab, '<HTML>', '<HEAD>'])
         if mm_cfg.IMAGE_LOGOS:
             output.append('<LINK REL="SHORTCUT ICON" HREF="%s">' %
                           (mm_cfg.IMAGE_LOGOS + mm_cfg.SHORTCUT_ICON))
         # Hit all the bases
         output.append('<META http-equiv="Content-Type" '
                       'content="text/html; charset=%s">' % charset)
         if self.title:
             output.append('%s<TITLE>%s</TITLE>' % (tab, self.title))
         output.append('%s</HEAD>' % tab)
         quals = []
         # Default link colors
         if mm_cfg.WEB_VLINK_COLOR:
             kws.setdefault('vlink', mm_cfg.WEB_VLINK_COLOR)
         if mm_cfg.WEB_ALINK_COLOR:
             kws.setdefault('alink', mm_cfg.WEB_ALINK_COLOR)
         if mm_cfg.WEB_LINK_COLOR:
             kws.setdefault('link', mm_cfg.WEB_LINK_COLOR)
         for k, v in kws.items():
             quals.append('%s="%s"' % (k, v))
         output.append('%s<BODY %s' % (tab, SPACE.join(quals)))
         # Language direction
         direction = Utils.GetDirection(self.language)
         output.append('dir="%s">' % direction)
     # Always do this...
     output.append(Container.Format(self, indent))
     if not self.suppress_head:
         output.append('%s</BODY>' % tab)
         output.append('%s</HTML>' % tab)
     return NL.join(output)
Пример #22
0
 def as_text(self):
     d = self.__dict__.copy()
     # We need to guarantee a valid From_ line, even if there are
     # bososities in the headers.
     if not d.get('fromdate', '').strip():
         d['fromdate'] = time.ctime(time.time())
     if not d.get('email', '').strip():
         d['email'] = '*****@*****.**'
     if not d.get('datestr', '').strip():
         d['datestr'] = time.ctime(time.time())
     #
     headers = [
         'From %(email)s  %(fromdate)s', 'From: %(email)s (%(author)s)',
         'Date: %(datestr)s', 'Subject: %(subject)s'
     ]
     if d['_in_reply_to']:
         headers.append('In-Reply-To: %(_in_reply_to)s')
     if d['_references']:
         headers.append('References: %(_references)s')
     if d['_message_id']:
         headers.append('Message-ID: %(_message_id)s')
     body = EMPTYSTRING.join(self.body)
     cset = Utils.GetCharSet(self._lang)
     # Coerce the body to Unicode and replace any invalid characters.
     if not isinstance(body, types.UnicodeType):
         body = unicode(body, cset, 'replace')
     if mm_cfg.ARCHIVER_OBSCURES_EMAILADDRS:
         otrans = i18n.get_translation()
         try:
             i18n.set_language(self._lang)
             atmark = unicode(_(' at '), cset)
             body = re.sub(r'([-+,.\w]+)@([-+.\w]+)',
                           '\g<1>' + atmark + '\g<2>', body)
         finally:
             i18n.set_translation(otrans)
     # Return body to character set of article.
     body = body.encode(cset, 'replace')
     return NL.join(headers) % d + '\n\n' + body + '\n'
Пример #23
0
    def __init__(self, maillist):
        # can't init the database while other processes are writing to it!
        # XXX TODO- implement native locking
        # with mailman's LockFile module for HyperDatabase.HyperDatabase
        #
        dir = maillist.archive_dir()
        db = HyperDatabase.HyperDatabase(dir, maillist)
        self.__super_init(dir, reload=1, database=db)

        self.maillist = maillist
        self._lock_file = None
        self.lang = maillist.preferred_language
        self.charset = Utils.GetCharSet(maillist.preferred_language)

        if hasattr(self.maillist, 'archive_volume_frequency'):
            if self.maillist.archive_volume_frequency == 0:
                self.ARCHIVE_PERIOD = 'year'
            elif self.maillist.archive_volume_frequency == 2:
                self.ARCHIVE_PERIOD = 'quarter'
            elif self.maillist.archive_volume_frequency == 3:
                self.ARCHIVE_PERIOD = 'week'
            elif self.maillist.archive_volume_frequency == 4:
                self.ARCHIVE_PERIOD = 'day'
            else:
                self.ARCHIVE_PERIOD = 'month'

        yre = r'(?P<year>[0-9]{4,4})'
        mre = r'(?P<month>[01][0-9])'
        dre = r'(?P<day>[0123][0-9])'
        self._volre = {
            'year': '^' + yre + '$',
            'quarter': '^' + yre + r'q(?P<quarter>[1234])$',
            'month': '^' + yre + r'-(?P<month>[a-zA-Z]+)$',
            'week': r'^Week-of-Mon-' + yre + mre + dre,
            'day': '^' + yre + mre + dre + '$'
        }
Пример #24
0
    def send_response(self):
        # Helper
        def indent(lines):
            return ['    ' + line for line in lines]

        # Quick exit for some commands which don't need a response
        if not self.respond:
            return
        resp = [
            Utils.wrap(
                _("""\
The results of your email command are provided below.
Attached is your original message.
"""))
        ]
        if self.results:
            resp.append(_('- Results:'))
            resp.extend(indent(self.results))
        # Ignore empty lines
        unprocessed = [
            line for line in self.commands[self.lineno:]
            if line and line.strip()
        ]
        if unprocessed and mm_cfg.RESPONSE_INCLUDE_LEVEL >= 2:
            resp.append(_('\n- Unprocessed:'))
            resp.extend(indent(unprocessed))
        if not unprocessed and not self.results:
            # The user sent an empty message; return a helpful one.
            resp.append(
                Utils.wrap(
                    _("""\
No commands were found in this message.
To obtain instructions, send a message containing just the word "help".
""")))
        if self.ignored and mm_cfg.RESPONSE_INCLUDE_LEVEL >= 2:
            resp.append(_('\n- Ignored:'))
            resp.extend(indent(self.ignored))
        resp.append(_('\n- Done.\n\n'))
        # Encode any unicode strings into the list charset, so we don't try to
        # join unicode strings and invalid ASCII.
        charset = Utils.GetCharSet(self.msgdata['lang'])
        encoded_resp = []
        for item in resp:
            if isinstance(item, UnicodeType):
                item = item.encode(charset, 'replace')
            encoded_resp.append(item)
        results = MIMEText(NL.join(encoded_resp), _charset=charset)
        # Safety valve for mail loops with misconfigured email 'bots.  We
        # don't respond to commands sent with "Precedence: bulk|junk|list"
        # unless they explicitly "X-Ack: yes", but not all mail 'bots are
        # correctly configured, so we max out the number of responses we'll
        # give to an address in a single day.
        #
        # BAW: We wait until now to make this decision since our sender may
        # not be self.msg.get_sender(), but I'm not sure this is right.
        recip = self.returnaddr or self.msg.get_sender()
        if not self.mlist.autorespondToSender(recip, self.msgdata['lang']):
            return
        msg = Message.UserNotification(recip,
                                       self.mlist.GetOwnerEmail(),
                                       _('The results of your email commands'),
                                       lang=self.msgdata['lang'])
        msg.set_type('multipart/mixed')
        msg.attach(results)
        if mm_cfg.RESPONSE_INCLUDE_LEVEL == 1:
            self.msg.set_payload(
                _('Message body suppressed by Mailman site configuration\n'))
        if mm_cfg.RESPONSE_INCLUDE_LEVEL == 0:
            orig = MIMEText(_(
                'Original message suppressed by Mailman site configuration\n'),
                            _charset=charset)
        else:
            orig = MIMEMessage(self.msg)
        msg.attach(orig)
        msg.send(self.mlist)
Пример #25
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)
Пример #26
0
def NewVars(l):
    """Add defaults for these new variables if they don't exist."""
    def add_only_if_missing(attr, initval, l=l):
        if not hasattr(l, attr):
            setattr(l, attr, initval)

    # 1.2 beta 1, baw 18-Feb-2000
    # Autoresponder mixin class attributes
    add_only_if_missing('autorespond_postings', 0)
    add_only_if_missing('autorespond_admin', 0)
    add_only_if_missing('autorespond_requests', 0)
    add_only_if_missing('autoresponse_postings_text', '')
    add_only_if_missing('autoresponse_admin_text', '')
    add_only_if_missing('autoresponse_request_text', '')
    add_only_if_missing('autoresponse_graceperiod', 90)
    add_only_if_missing('postings_responses', {})
    add_only_if_missing('admin_responses', {})
    add_only_if_missing('reply_goes_to_list', '')
    add_only_if_missing('preferred_language', mm_cfg.DEFAULT_SERVER_LANGUAGE)
    add_only_if_missing('available_languages', [])
    add_only_if_missing('digest_volume_frequency',
                        mm_cfg.DEFAULT_DIGEST_VOLUME_FREQUENCY)
    add_only_if_missing('digest_last_sent_at', 0)
    add_only_if_missing('mod_password', None)
    add_only_if_missing('post_password', None)
    add_only_if_missing('moderator', [])
    add_only_if_missing('topics', [])
    add_only_if_missing('topics_enabled', 0)
    add_only_if_missing('topics_bodylines_limit', 5)
    add_only_if_missing('one_last_digest', {})
    add_only_if_missing('usernames', {})
    add_only_if_missing('personalize', 0)
    add_only_if_missing('first_strip_reply_to',
                        mm_cfg.DEFAULT_FIRST_STRIP_REPLY_TO)
    add_only_if_missing('subscribe_auto_approval',
                        mm_cfg.DEFAULT_SUBSCRIBE_AUTO_APPROVAL)
    add_only_if_missing('unsubscribe_policy',
                        mm_cfg.DEFAULT_UNSUBSCRIBE_POLICY)
    add_only_if_missing('send_goodbye_msg', mm_cfg.DEFAULT_SEND_GOODBYE_MSG)
    add_only_if_missing('include_rfc2369_headers', 1)
    add_only_if_missing('include_list_post_header', 1)
    add_only_if_missing('include_sender_header', 1)
    add_only_if_missing('bounce_score_threshold',
                        mm_cfg.DEFAULT_BOUNCE_SCORE_THRESHOLD)
    add_only_if_missing('bounce_info_stale_after',
                        mm_cfg.DEFAULT_BOUNCE_INFO_STALE_AFTER)
    add_only_if_missing('bounce_you_are_disabled_warnings',
                        mm_cfg.DEFAULT_BOUNCE_YOU_ARE_DISABLED_WARNINGS)
    add_only_if_missing(
        'bounce_you_are_disabled_warnings_interval',
        mm_cfg.DEFAULT_BOUNCE_YOU_ARE_DISABLED_WARNINGS_INTERVAL)
    add_only_if_missing('bounce_unrecognized_goes_to_list_owner',
                        mm_cfg.DEFAULT_BOUNCE_UNRECOGNIZED_GOES_TO_LIST_OWNER)
    add_only_if_missing('bounce_notify_owner_on_bounce_increment',
                        mm_cfg.DEFAULT_BOUNCE_NOTIFY_OWNER_ON_BOUNCE_INCREMENT)
    add_only_if_missing('bounce_notify_owner_on_disable',
                        mm_cfg.DEFAULT_BOUNCE_NOTIFY_OWNER_ON_DISABLE)
    add_only_if_missing('bounce_notify_owner_on_removal',
                        mm_cfg.DEFAULT_BOUNCE_NOTIFY_OWNER_ON_REMOVAL)
    add_only_if_missing('ban_list', [])
    add_only_if_missing('filter_mime_types', mm_cfg.DEFAULT_FILTER_MIME_TYPES)
    add_only_if_missing('pass_mime_types', mm_cfg.DEFAULT_PASS_MIME_TYPES)
    add_only_if_missing('filter_content', mm_cfg.DEFAULT_FILTER_CONTENT)
    add_only_if_missing('convert_html_to_plaintext',
                        mm_cfg.DEFAULT_CONVERT_HTML_TO_PLAINTEXT)
    add_only_if_missing('filter_action', mm_cfg.DEFAULT_FILTER_ACTION)
    add_only_if_missing('delivery_status', {})
    # This really ought to default to mm_cfg.HOLD, but that doesn't work with
    # the current GUI description model.  So, 0==Hold, 1==Reject, 2==Discard
    add_only_if_missing('member_moderation_action', 0)
    add_only_if_missing('member_moderation_notice', '')
    add_only_if_missing('dmarc_moderation_action',
                        mm_cfg.DEFAULT_DMARC_MODERATION_ACTION)
    add_only_if_missing('dmarc_quarantine_moderation_action',
                        mm_cfg.DEFAULT_DMARC_QUARANTINE_MODERATION_ACTION)
    add_only_if_missing('dmarc_none_moderation_action',
                        mm_cfg.DEFAULT_DMARC_NONE_MODERATION_ACTION)
    add_only_if_missing('dmarc_moderation_notice', '')
    add_only_if_missing('dmarc_wrapped_message_text',
                        mm_cfg.DEFAULT_DMARC_WRAPPED_MESSAGE_TEXT)
    add_only_if_missing('member_verbosity_threshold',
                        mm_cfg.DEFAULT_MEMBER_VERBOSITY_THRESHOLD)
    add_only_if_missing('member_verbosity_interval',
                        mm_cfg.DEFAULT_MEMBER_VERBOSITY_INTERVAL)
    add_only_if_missing('equivalent_domains',
                        mm_cfg.DEFAULT_EQUIVALENT_DOMAINS)
    add_only_if_missing('new_member_options',
                        mm_cfg.DEFAULT_NEW_MEMBER_OPTIONS)
    # Emergency moderation flag
    add_only_if_missing('emergency', 0)
    add_only_if_missing('hold_and_cmd_autoresponses', {})
    add_only_if_missing('news_prefix_subject_too', 1)
    # Should prefixes be encoded?
    if Utils.GetCharSet(l.preferred_language) == 'us-ascii':
        encode = 0
    else:
        encode = 2
    add_only_if_missing('encode_ascii_prefixes', encode)
    add_only_if_missing('news_moderation', 0)
    add_only_if_missing('header_filter_rules', [])
    # Scrubber in regular delivery
    add_only_if_missing('scrub_nondigest', 0)
    # ContentFilter by file extensions
    add_only_if_missing('filter_filename_extensions',
                        mm_cfg.DEFAULT_FILTER_FILENAME_EXTENSIONS)
    add_only_if_missing('pass_filename_extensions', [])
    # automatic discard
    add_only_if_missing('max_days_to_hold', 0)
    add_only_if_missing('nonmember_rejection_notice', '')
    # multipart/alternative collapse
    add_only_if_missing('collapse_alternatives',
                        mm_cfg.DEFAULT_COLLAPSE_ALTERNATIVES)
    # exclude/include lists
    add_only_if_missing('regular_exclude_lists',
                        mm_cfg.DEFAULT_REGULAR_EXCLUDE_LISTS)
    add_only_if_missing('regular_include_lists',
                        mm_cfg.DEFAULT_REGULAR_INCLUDE_LISTS)
    add_only_if_missing('regular_exclude_ignore',
                        mm_cfg.DEFAULT_REGULAR_EXCLUDE_IGNORE)
Пример #27
0
 def _handleForm(self, mlist, category, subcat, cgidata, doc):
     # TK: If there is no hdrfilter_* in cgidata, we should not touch
     # the header filter rules.
     if not cgidata.has_key('hdrfilter_rebox_01'):
         return
     # First deal with
     rules = []
     # We start i at 1 and keep going until we no longer find items keyed
     # with the marked tags.
     i = 1
     downi = None
     while True:
         deltag = 'hdrfilter_delete_%02d' % i
         reboxtag = 'hdrfilter_rebox_%02d' % i
         actiontag = 'hdrfilter_action_%02d' % i
         wheretag = 'hdrfilter_where_%02d' % i
         addtag = 'hdrfilter_add_%02d' % i
         newtag = 'hdrfilter_new_%02d' % i
         uptag = 'hdrfilter_up_%02d' % i
         downtag = 'hdrfilter_down_%02d' % i
         i += 1
         # Was this a delete?  If so, we can just ignore this entry
         if cgidata.has_key(deltag):
             continue
         # Get the data for the current box
         pattern = cgidata.getfirst(reboxtag)
         try:
             action = int(cgidata.getfirst(actiontag))
             # We'll get a TypeError when the actiontag is missing and the
             # .getvalue() call returns None.
         except (ValueError, TypeError):
             action = mm_cfg.DEFER
         if pattern is None:
             # We came to the end of the boxes
             break
         if cgidata.has_key(newtag) and not pattern:
             # This new entry is incomplete.
             if i == 2:
                 # OK it is the first.
                 continue
             doc.addError(
                 _("""Header filter rules require a pattern.
             Incomplete filter rules will be ignored."""))
             continue
         # Make sure the pattern was a legal regular expression.
         # Convert it to unicode if necessary.
         mo = re.match('.*charset=([-_a-z0-9]+)',
                       os.environ.get('CONTENT_TYPE', ''), re.IGNORECASE)
         if mo:
             cset = mo.group(1)
         else:
             cset = Utils.GetCharSet(mlist.preferred_language)
         try:
             upattern = Utils.xml_to_unicode(pattern, cset)
             re.compile(upattern)
             pattern = upattern
         except (re.error, TypeError):
             safepattern = Utils.websafe(pattern)
             doc.addError(
                 _("""The header filter rule pattern
             '%(safepattern)s' is not a legal regular expression.  This
             rule will be ignored."""))
             continue
         # Was this an add item?
         if cgidata.has_key(addtag):
             # Where should the new one be added?
             where = cgidata.getfirst(wheretag)
             if where == 'before':
                 # Add a new empty rule box before the current one
                 rules.append(('', mm_cfg.DEFER, True))
                 rules.append((pattern, action, False))
                 # Default is to add it after...
             else:
                 rules.append((pattern, action, False))
                 rules.append(('', mm_cfg.DEFER, True))
         # Was this an up movement?
         elif cgidata.has_key(uptag):
             # As long as this one isn't the first rule, move it up
             if rules:
                 rules.insert(-1, (pattern, action, False))
             else:
                 rules.append((pattern, action, False))
         # Was this the down movement?
         elif cgidata.has_key(downtag):
             downi = i - 2
             rules.append((pattern, action, False))
         # Otherwise, just retain this one in the list
         else:
             rules.append((pattern, action, False))
     # Move any down button filter rule
     if downi is not None:
         rule = rules[downi]
         del rules[downi]
         rules.insert(downi + 1, rule)
     mlist.header_filter_rules = rules
Пример #28
0
 def _decode(h):
     if not h:
         return h
     return Utils.oneline(h, Utils.GetCharSet(mlist.preferred_language))
Пример #29
0
 def __processbody_URLquote(self, lines):
     # XXX a lot to do here:
     # 1. use lines directly, rather than source and dest
     # 2. make it clearer
     # 3. make it faster
     # TK: Prepare for unicode obscure.
     atmark = _(' at ')
     if lines and isinstance(lines[0], types.UnicodeType):
         atmark = unicode(atmark, Utils.GetCharSet(self.lang), 'replace')
     source = lines[:]
     dest = lines
     last_line_was_quoted = 0
     for i in xrange(0, len(source)):
         Lorig = L = source[i]
         prefix = suffix = ""
         if L is None:
             continue
         # Italicise quoted text
         if self.IQUOTES:
             quoted = quotedpat.match(L)
             if quoted is None:
                 last_line_was_quoted = 0
             else:
                 quoted = quoted.end(0)
                 prefix = CGIescape(L[:quoted], self.lang) + '<i>'
                 suffix = '</I>'
                 if self.SHOWHTML:
                     suffix += '<BR>'
                     if not last_line_was_quoted:
                         prefix = '<BR>' + prefix
                 L = L[quoted:]
                 last_line_was_quoted = 1
         # Check for an e-mail address
         L2 = ""
         jr = emailpat.search(L)
         kr = urlpat.search(L)
         while jr is not None or kr is not None:
             if jr == None:
                 j = -1
             else:
                 j = jr.start(0)
             if kr is None:
                 k = -1
             else:
                 k = kr.start(0)
             if j != -1 and (j < k or k == -1):
                 text = jr.group(1)
                 length = len(text)
                 if mm_cfg.ARCHIVER_OBSCURES_EMAILADDRS:
                     text = re.sub('@', atmark, text)
                     URL = self.maillist.GetScriptURL('listinfo',
                                                      absolute=1)
                 else:
                     URL = 'mailto:' + text
                 pos = j
             elif k != -1 and (j > k or j == -1):
                 text = URL = kr.group(1)
                 length = len(text)
                 pos = k
             else:  # j==k
                 raise ValueError, "j==k: This can't happen!"
             #length = len(text)
             #self.message("URL: %s %s %s \n"
             #             % (CGIescape(L[:pos]), URL, CGIescape(text)))
             L2 += '%s<A HREF="%s">%s</A>' % (CGIescape(
                 L[:pos],
                 self.lang), html_quote(URL), CGIescape(text, self.lang))
             L = L[pos + length:]
             jr = emailpat.search(L)
             kr = urlpat.search(L)
         if jr is None and kr is None:
             L = CGIescape(L, self.lang)
         L = prefix + L2 + L + suffix
         source[i] = None
         dest[i] = L
Пример #30
0
    def __init__(self,
                 message=None,
                 sequence=0,
                 keepHeaders=[],
                 lang=mm_cfg.DEFAULT_SERVER_LANGUAGE,
                 mlist=None):
        self.__super_init(message, sequence, keepHeaders)
        self.prev = None
        self.next = None
        # Trim Re: from the subject line
        i = 0
        while i != -1:
            result = REpat.match(self.subject)
            if result:
                i = result.end(0)
                self.subject = self.subject[i:]
            else:
                i = -1
        # Useful to keep around
        self._lang = lang
        self._mlist = mlist

        if mm_cfg.ARCHIVER_OBSCURES_EMAILADDRS:
            # Avoid i18n side-effects.  Note that the language for this
            # article (for this list) could be different from the site-wide
            # preferred language, so we need to ensure no side-effects will
            # occur.  Think what happens when executing bin/arch.
            otrans = i18n.get_translation()
            try:
                i18n.set_language(lang)
                if self.author == self.email:
                    self.author = self.email = re.sub('@', _(' at '),
                                                      self.email)
                else:
                    self.email = re.sub('@', _(' at '), self.email)
            finally:
                i18n.set_translation(otrans)

        # Snag the content-* headers.  RFC 1521 states that their values are
        # case insensitive.
        ctype = message.get('Content-Type', 'text/plain')
        cenc = message.get('Content-Transfer-Encoding', '')
        self.ctype = ctype.lower()
        self.cenc = cenc.lower()
        self.decoded = {}
        cset = Utils.GetCharSet(mlist.preferred_language)
        cset_out = Charset(cset).output_charset or cset
        if isinstance(cset_out, unicode):
            # email 3.0.1 (python 2.4) doesn't like unicode
            cset_out = cset_out.encode('us-ascii')
        charset = message.get_content_charset(cset_out)
        if charset:
            charset = charset.lower().strip()
            if charset[0] == '"' and charset[-1] == '"':
                charset = charset[1:-1]
            if charset[0] == "'" and charset[-1] == "'":
                charset = charset[1:-1]
            try:
                body = message.get_payload(decode=True)
            except binascii.Error:
                body = None
            if body and charset != Utils.GetCharSet(self._lang):
                # decode body
                try:
                    body = unicode(body, charset)
                except (UnicodeError, LookupError):
                    body = None
            if body:
                self.body = [l + "\n" for l in body.splitlines()]

        self.decode_headers()