Пример #1
0
def topic_details(mlist, doc, user, cpuser, userlang, varhelp):
    # Find out which topic the user wants to get details of
    reflist = varhelp.split("/")
    name = None
    topicname = _("<missing>")
    if len(reflist) == 1:
        topicname = urllib.unquote_plus(reflist[0])
        for name, pattern, description, emptyflag in mlist.topics:
            if name == topicname:
                break
        else:
            name = None

    if not name:
        options_page(mlist, doc, user, cpuser, userlang, _("Requested topic is not valid: %(topicname)s"))
        print doc.Format()
        return

    table = Table(border=3, width="100%")
    table.AddRow([Center(Bold(_("Topic filter details")))])
    table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2, bgcolor=mm_cfg.WEB_SUBHEADER_COLOR)
    table.AddRow([Bold(Label(_("Name:"))), Utils.websafe(name)])
    table.AddRow([Bold(Label(_("Pattern (as regexp):"))), "<pre>" + Utils.websafe(pattern) + "</pre>"])
    table.AddRow([Bold(Label(_("Description:"))), Utils.websafe(description)])
    # Make colors look nice
    for row in range(1, 4):
        table.AddCellInfo(row, 0, bgcolor=mm_cfg.WEB_ADMINITEM_COLOR)

    options_page(mlist, doc, user, cpuser, userlang, table.Format())
    print doc.Format()
Пример #2
0
def topic_details(mlist, doc, user, cpuser, userlang, varhelp):
    # Find out which topic the user wants to get details of
    reflist = varhelp.split('/')
    name = None
    topicname = _('<missing>')
    if len(reflist) == 1:
        topicname = urllib.unquote_plus(reflist[0])
        for name, pattern, description, emptyflag in mlist.topics:
            if name == topicname:
                break
        else:
            name = None

    if not name:
        options_page(mlist, doc, user, cpuser, userlang,
                     _('Requested topic is not valid: %(topicname)s'))
        print doc.Format()
        return

    table = Table(border=3, width='100%')
    table.AddRow([Center(Bold(_('Topic filter details')))])
    table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2,
                      bgcolor=mm_cfg.WEB_SUBHEADER_COLOR)
    table.AddRow([Bold(Label(_('Name:'))),
                  Utils.websafe(name)])
    table.AddRow([Bold(Label(_('Pattern (as regexp):'))),
                  '<pre>' + Utils.websafe(pattern) + '</pre>'])
    table.AddRow([Bold(Label(_('Description:'))),
                  Utils.websafe(description)])
    # Make colors look nice
    for row in range(1, 4):
        table.AddCellInfo(row, 0, bgcolor=mm_cfg.WEB_ADMINITEM_COLOR)

    options_page(mlist, doc, user, cpuser, userlang, table.Format())
    print doc.Format()
Пример #3
0
 def Format(self, indent=0):
     # If I don't start a new I ignore indent
     output = '<%s>%s</%s>' % (
         self.tag,
         Utils.websafe(Container.Format(self, indent)),
         self.tag)
     return output
Пример #4
0
    def scrub_html1(self, part):
        # sanitize == 1
        payload = Utils.websafe(part.get_payload(decode=True))
        # For whitespace in the margin, change spaces into
        # non-breaking spaces, and tabs into 8 of those.  Then use a
        # mono-space font.  Still looks hideous to me, but then I'd
        # just as soon discard them.
        def doreplace(s):
            return s.expandtabs(8).replace(' ', '&nbsp;')
        lines = [doreplace(s) for s in payload.split('\n')]
        payload = '<tt>\n' + BR.join(lines) + '\n</tt>\n'
        part.set_payload(payload)
        # We're replacing the payload with the decoded payload so this
        # will just get in the way.
        del part['content-transfer-encoding']
        omask = os.umask(002)
        try:
            url = save_attachment(self.mlist, part, self.dir,
                                  filter_html=False)
        finally:
            os.umask(omask)
        self.msgtexts.append(unicode(_("""\
An HTML attachment was scrubbed...
URL: %(url)s
"""), self.lcset))
Пример #5
0
 def FormatBox(self, name, size=20, value=''):
     if isinstance(value, str):
         safevalue = Utils.websafe(value)
     else:
         safevalue = value
     return '<INPUT type="Text" name="%s" size="%d" value="%s">' % (
         name, size, safevalue)
Пример #6
0
    def handler (self, parts):
        lists = []
        for name, mlist in self.all_mls.iteritems():
            members = mlist.getRegularMemberKeys()
            subscribed = True if self.curr_user in members else False

            if not mlist.advertised and not subscribed:
                continue

            lists.append({'script_url'  : mlist.GetScriptURL('listinfo'),
                          'real_name'   : mlist.real_name,
                          'description' : Utils.websafe(mlist.description),
                          'subscribed'  : subscribed,
                          'owners'      : ', '.join(mlist.owner),
                          'owner-email' : mlist.GetOwnerEmail(),
                          'advertised'  : mlist.advertised,
                          })
    
        self.kwargs_add('lists', lists)

        if len(parts) > 0:
            try:
                self.add_req_ln_details(parts[0].strip())
            except:
                self.kwargs_add('vl_ln', None)
        else:
            self.kwargs_add('vl_ln', None)

        self.render()
 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()
     d = {
         '<mm-mailman-footer>' : self.GetMailmanFooter(),
         '<mm-list-name>' : self.real_name,
         '<mm-email-user>' : self._internal_name,
         '<mm-list-description>' : Utils.websafe(self.description),
         '<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>' : `member_len`,
         '<mm-num-digesters>' : `dmember_len`,
         '<mm-num-members>' : (`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,
         }
Пример #8
0
def main():
    doc = Document()
    doc.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE)

    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, 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', 'No such list "%s": %s\n', listname, e)
        return
def FormatHidden(name, value='display'):
        if isinstance(value, str):
            safevalue = Utils.websafe(value)
        else:
            safevalue = value
        return '<INPUT type="Hidden" name="%s" value="%s">' % (
            name, safevalue)
Пример #10
0
def main():
    doc = Document()
    doc.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE)

    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, 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", 'No such list "%s": %s\n', listname, e)
        return
Пример #11
0
def display(mlist, captchas):
    """Returns a CAPTCHA question, the HTML for the answer box, and
    the data to be put into the CSRF token"""
    idx = random.randrange(len(captchas))
    question = captchas[idx][0]
    box_html = mlist.FormatBox('captcha_answer', size=30)
    return (Utils.websafe(question), box_html, str(idx))
 def FormatBox(self, name, size=20, value=''):
     if isinstance(value, str):
         safevalue = Utils.websafe(value)
     else:
         safevalue = value
     return '<INPUT type="Text" name="%s" size="%d" value="%s">' % (
         name, size, safevalue)
Пример #13
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()
     d = {
         '<mm-mailman-footer>' : self.GetMailmanFooter(),
         '<mm-list-name>' : self.real_name,
         '<mm-email-user>' : self._internal_name,
         '<mm-list-description>' : Utils.websafe(self.description),
         '<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>' : `member_len`,
         '<mm-num-digesters>' : `dmember_len`,
         '<mm-num-members>' : (`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,
         }
Пример #14
0
    def errcheck (self, action):
        """Performs all error checks. Returns None is all's good. Otherwise
        returns a string with error message."""

        if not can_create_lists(self.curr_user):
            return 'You are not authorized to creates lists on this server'

        if len(self.owner) <= 0:
            return 'Cannot create list without a owner.'

        if self.ln == '':
            return 'You forgot to enter the list name'

        if '@' in self.ln:
            return 'List name must not include "@": %s' % self.safeln

        if action == 'create' and Utils.list_exists(self.ln):
            return 'List already exists: %s' % self.safe_ln

        if mm_cfg.VIRTUAL_HOST_OVERVIEW and \
          not mm_cfg.VIRTUAL_HOSTS.has_key(self.hn):
            safehostname = Utils.websafe(self.hn)
            return 'Unknown virtual host: %s' % safehostname

        return None
Пример #15
0
def main():
    doc = Document()
    doc.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE)

    cgidata = cgi.FieldStorage()
    parts = Utils.GetPathPieces()

    if not parts:
        # Bad URL specification
        title = _('Bad URL specification')
        doc.SetTitle(title)
        doc.AddItem(
            Header(3, Bold(FontAttr(title, color='#ff0000', size='+2'))))
        doc.AddItem('<hr>')
        doc.AddItem(MailmanLogo())
        print doc.Format()
        syslog('error', 'Bad URL specification: %s', parts)
        return
        
    listname = parts[0].lower()
    try:
        mlist = MailList.MailList(listname, lock=0)
    except Errors.MMListError, e:
        # Avoid cross-site scripting attacks
        safelistname = Utils.websafe(listname)
        title = _('No such list <em>%(safelistname)s</em>')
        doc.SetTitle(title)
        doc.AddItem(
            Header(3,
                   Bold(FontAttr(title, color='#ff0000', size='+2'))))
        doc.AddItem('<hr>')
        doc.AddItem(MailmanLogo())
        print doc.Format()
        syslog('error', 'No such list "%s": %s\n', listname, e)
        return
Пример #16
0
def main():
    doc = Document()
    doc.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE)

    parts = Utils.GetPathPieces()
    if not parts:
        doc.SetTitle(_("Private Archive Error"))
        doc.AddItem(Header(3, _("You must specify a list.")))
        print doc.Format()
        return

    path = os.environ.get('PATH_INFO')
    tpath = true_path(path)
    if tpath <> path[1:]:
        msg = _('Private archive - "./" and "../" not allowed in URL.')
        doc.SetTitle(msg)
        doc.AddItem(Header(2, msg))
        print doc.Format()
        syslog('mischief', 'Private archive hostile path: %s', path)
        return
    # BAW: This needs to be converted to the Site module abstraction
    true_filename = os.path.join(
        mm_cfg.PRIVATE_ARCHIVE_FILE_DIR, tpath)

    listname = parts[0].lower()
    mboxfile = ''
    if len(parts) > 1:
        mboxfile = parts[1]

    # See if it's the list's mbox file is being requested
    if listname.endswith('.mbox') and mboxfile.endswith('.mbox') and \
           listname[:-5] == mboxfile[:-5]:
        listname = listname[:-5]
    else:
        mboxfile = ''

    # If it's a directory, we have to append index.html in this script.  We
    # must also check for a gzipped file, because the text archives are
    # usually stored in compressed form.
    if os.path.isdir(true_filename):
        true_filename = true_filename + '/index.html'
    if not os.path.exists(true_filename) and \
           os.path.exists(true_filename + '.gz'):
        true_filename = true_filename + '.gz'

    try:
        mlist = MailList.MailList(listname, lock=0)
    except Errors.MMListError, e:
        # Avoid cross-site scripting attacks
        safelistname = Utils.websafe(listname)
        msg = _('No such list <em>%(safelistname)s</em>')
        doc.SetTitle(_("Private Archive Error - %(msg)s"))
        doc.AddItem(Header(2, msg))
        # Send this with a 404 status.
        print 'Status: 404 Not Found'
        print doc.Format()
        syslog('error', 'private: No such list "%s": %s\n', listname, e)
        return
Пример #17
0
def main():
    doc = Document()
    doc.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE)

    parts = Utils.GetPathPieces()
    if not parts:
        doc.SetTitle(_("Private Archive Error"))
        doc.AddItem(Header(3, _("You must specify a list.")))
        print doc.Format()
        return

    path = os.environ.get('PATH_INFO')
    tpath = true_path(path)
    if tpath <> path[1:]:
        msg = _('Private archive - "./" and "../" not allowed in URL.')
        doc.SetTitle(msg)
        doc.AddItem(Header(2, msg))
        print doc.Format()
        syslog('mischief', 'Private archive hostile path: %s', path)
        return
    # BAW: This needs to be converted to the Site module abstraction
    true_filename = os.path.join(
        mm_cfg.PRIVATE_ARCHIVE_FILE_DIR, tpath)

    listname = parts[0].lower()
    mboxfile = ''
    if len(parts) > 1:
        mboxfile = parts[1]

    # See if it's the list's mbox file is being requested
    if listname.endswith('.mbox') and mboxfile.endswith('.mbox') and \
           listname[:-5] == mboxfile[:-5]:
        listname = listname[:-5]
    else:
        mboxfile = ''

    # If it's a directory, we have to append index.html in this script.  We
    # must also check for a gzipped file, because the text archives are
    # usually stored in compressed form.
    if os.path.isdir(true_filename):
        true_filename = true_filename + '/index.html'
    if not os.path.exists(true_filename) and \
           os.path.exists(true_filename + '.gz'):
        true_filename = true_filename + '.gz'

    try:
        mlist = MailList.MailList(listname, lock=0)
    except Errors.MMListError, e:
        # Avoid cross-site scripting attacks
        safelistname = Utils.websafe(listname)
        msg = _('No such list <em>%(safelistname)s</em>')
        doc.SetTitle(_("Private Archive Error - %(msg)s"))
        doc.AddItem(Header(2, msg))
        # Send this with a 404 status.
        print 'Status: 404 Not Found'
        print doc.Format()
        syslog('error', 'private: No such list "%s": %s\n', listname, e)
        return
Пример #18
0
    def request_create (self):
        """Creates a list (name taken from the CGI form value called lc_name).

        Returns None if the list was created successfully. Returns a string
        containing error message if list could not be created for whatever
        reason."""

        self._ml = MailList.MailList()
        err = self.errcheck(action='create')
        if err:
            return err

        # We've got all the data we need, so go ahead and try to create the
        # list See admin.py for why we need to set up the signal handler.

        try:
            signal.signal(signal.SIGTERM, self.sigterm_handler)
            pwhex = sha_new(self.pw).hexdigest()

            # Guarantee that all newly created files have the proper permission.
            # proper group ownership should be assured by the autoconf script
            # enforcing that all directories have the group sticky bit set
            oldmask = os.umask(002)
            try:
                try:
                    self.ml.Create(self.ln, self.owner[0], pwhex, self.langs,
                                   self.eh, urlhost=self.hn)
                finally:
                    os.umask(oldmask)
            except Errors.EmailAddressError, e:
                if e.args:
                    s = Utils.websafe(e.args[0])
                else:
                    s = Utils.websafe(owner)

                return 'Bad owner email address: %s' % s
            except Errors.MMListAlreadyExistsError:
                return 'List already exists: %s' % self.ln
            except Errors.BadListNameError, e:
                if e.args:
                    s = Utils.websafe(e.args[0])
                else:
                    s = Utils.websafe(listname)
                return 'Illegal list name: %s' % self.ln
Пример #19
0
def show_pending_subs(mlist, form):
    # Add the subscription request section
    pendingsubs = mlist.GetSubscriptionIds()
    if not pendingsubs:
        return 0
    form.AddItem('<hr>')
    form.AddItem(Center(Header(2, _('Subscription Requests'))))
    table = Table(border=2)
    table.AddRow([Center(Bold(_('Address/name/time'))),
                  Center(Bold(_('Your decision'))),
                  Center(Bold(_('Reason for refusal')))
                  ])
    # Alphabetical order by email address
    byaddrs = {}
    for id in pendingsubs:
        addr = mlist.GetRecord(id)[1]
        byaddrs.setdefault(addr, []).append(id)
    addrs = byaddrs.items()
    addrs.sort()
    num = 0
    for addr, ids in addrs:
        # Eliminate duplicates.
        # The list ws returned sorted ascending.  Keep the last.
        for id in ids[:-1]:
            mlist.HandleRequest(id, mm_cfg.DISCARD)
        id = ids[-1]
        stime, addr, fullname, passwd, digest, lang = mlist.GetRecord(id)
        fullname = Utils.uncanonstr(fullname, mlist.preferred_language)
        displaytime = time.ctime(stime)
        radio = RadioButtonArray(id, (_('Defer'),
                                      _('Approve'),
                                      _('Reject'),
                                      _('Discard')),
                                 values=(mm_cfg.DEFER,
                                         mm_cfg.SUBSCRIBE,
                                         mm_cfg.REJECT,
                                         mm_cfg.DISCARD),
                                 checked=0).Format()
        if addr not in mlist.ban_list:
            radio += ('<br>' + '<label>' +
                     CheckBox('ban-%d' % id, 1).Format() +
                     '&nbsp;' + _('Permanently ban from this list') +
                     '</label>')
        # While the address may be a unicode, it must be ascii
        paddr = addr.encode('us-ascii', 'replace')
        table.AddRow(['%s<br><em>%s</em><br>%s' % (paddr,
                                                   Utils.websafe(fullname),
                                                   displaytime),
                      radio,
                      TextBox('comment-%d' % id, size=40)
                      ])
        num += 1
    if num > 0:
        form.AddItem(table)
    return num
Пример #20
0
 def __init__(self, name, text='', rows=None, cols=None, wrap='soft',
              readonly=0):
     if isinstance(text, str):
         safetext = Utils.websafe(text)
     else:
         safetext = text
     self.name = name
     self.text = safetext
     self.rows = rows
     self.cols = cols
     self.wrap = wrap
     self.readonly = readonly
Пример #21
0
def show_pending_unsubs(mlist, form):
    # Add the pending unsubscription request section
    lang = mlist.preferred_language
    pendingunsubs = mlist.GetUnsubscriptionIds()
    if not pendingunsubs:
        return 0
    table = Table(border=2)
    table.AddRow([Center(Bold(_('User address/name'))),
                  Center(Bold(_('Your decision'))),
                  Center(Bold(_('Reason for refusal')))
                  ])
    # Alphabetical order by email address
    byaddrs = {}
    for id in pendingunsubs:
        addr = mlist.GetRecord(id)
        byaddrs.setdefault(addr, []).append(id)
    addrs = byaddrs.keys()
    addrs.sort()
    num = 0
    for addr, ids in byaddrs.items():
        # Eliminate duplicates
        for id in ids[1:]:
            mlist.HandleRequest(id, mm_cfg.DISCARD)
        id = ids[0]
        addr = mlist.GetRecord(id)
        try:
            fullname = Utils.uncanonstr(mlist.getMemberName(addr), lang)
        except Errors.NotAMemberError:
            # They must have been unsubscribed elsewhere, so we can just
            # discard this record.
            mlist.HandleRequest(id, mm_cfg.DISCARD)
            continue
        num += 1
        # While the address may be a unicode, it must be ascii
        paddr = addr.encode('us-ascii', 'replace')
        table.AddRow(['%s<br><em>%s</em>' % (paddr, Utils.websafe(fullname)),
                      RadioButtonArray(id, (_('Defer'),
                                            _('Approve'),
                                            _('Reject'),
                                            _('Discard')),
                                       values=(mm_cfg.DEFER,
                                               mm_cfg.UNSUBSCRIBE,
                                               mm_cfg.REJECT,
                                               mm_cfg.DISCARD),
                                       checked=0),
                      TextBox('comment-%d' % id, size=45)
                      ])
    if num > 0:
        form.AddItem('<hr>')
        form.AddItem(Center(Header(2, _('Unsubscription Requests'))))
        form.AddItem(table)
    return num
Пример #22
0
def show_pending_subs(mlist, form):
    # Add the subscription request section
    pendingsubs = mlist.GetSubscriptionIds()
    if not pendingsubs:
        return 0
    form.AddItem('<hr>')
    form.AddItem(Center(Header(2, _('Subscription Requests'))))
    table = Table(border=2)
    table.AddRow([Center(Bold(_('Address/name'))),
                  Center(Bold(_('Your decision'))),
                  Center(Bold(_('Reason for refusal')))
                  ])
    # Alphabetical order by email address
    byaddrs = {}
    for id in pendingsubs:
        addr = mlist.GetRecord(id)[1]
        byaddrs.setdefault(addr, []).append(id)
    addrs = byaddrs.items()
    addrs.sort()
    num = 0
    for addr, ids in addrs:
        # Eliminate duplicates
        for id in ids[1:]:
            mlist.HandleRequest(id, mm_cfg.DISCARD)
        id = ids[0]
        time, addr, fullname, passwd, digest, lang = mlist.GetRecord(id)
        fullname = Utils.uncanonstr(fullname, mlist.preferred_language)
        radio = RadioButtonArray(id, (_('Defer'),
                                      _('Approve'),
                                      _('Reject'),
                                      _('Discard')),
                                 values=(mm_cfg.DEFER,
                                         mm_cfg.SUBSCRIBE,
                                         mm_cfg.REJECT,
                                         mm_cfg.DISCARD),
                                 checked=0).Format()
        if addr not in mlist.ban_list:
            radio += ('<br>' + '<label>' +
                     CheckBox('ban-%d' % id, 1).Format() +
                     '&nbsp;' + _('Permanently ban from this list') +
                     '</label>')
        # While the address may be a unicode, it must be ascii
        paddr = addr.encode('us-ascii', 'replace')
        table.AddRow(['%s<br><em>%s</em>' % (paddr, Utils.websafe(fullname)),
                      radio,
                      TextBox('comment-%d' % id, size=40)
                      ])
        num += 1
    if num > 0:
        form.AddItem(table)
    return num
Пример #23
0
def main():
    # Trick out pygettext since we want to mark template_data as translatable,
    # but we don't want to actually translate it here.
    def _(s):
        return s

    template_data = (
        ('listinfo.html', _('General list information page')),
        ('subscribe.html', _('Subscribe results page')),
        ('options.html', _('User specific options page')),
        ('subscribeack.txt', _('Welcome email text file')),
        ('masthead.txt', _('Digest masthead')),
        ('postheld.txt', _('User notice of held post')),
        ('approve.txt', _('User notice of held subscription')),
        ('refuse.txt', _('Notice of post refused by moderator')),
        ('invite.txt', _('Invitation to join list')),
        ('verify.txt', _('Request to confirm subscription')),
        ('unsub.txt', _('Request to confirm unsubscription')),
        ('nomoretoday.txt', _('User notice of autoresponse limit')),
        ('postack.txt', _('User post acknowledgement')),
        ('disabled.txt', _('Subscription disabled by bounce warning')),
        ('admlogin.html', _('Admin/moderator login page')),
        ('private.html', _('Private archive login page')),
        ('userpass.txt', _('On demand password reminder')),
    )

    _ = i18n._
    doc = Document()

    # Set up the system default language
    i18n.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE)
    doc.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE)

    parts = Utils.GetPathPieces()
    if not parts:
        doc.AddItem(Header(2, _("List name is required.")))
        print doc.Format()
        return

    listname = parts[0].lower()
    try:
        mlist = MailList.MailList(listname, lock=0)
    except Errors.MMListError, e:
        # Avoid cross-site scripting attacks
        safelistname = Utils.websafe(listname)
        doc.AddItem(Header(2, _('No such list <em>%(safelistname)s</em>')))
        # Send this with a 404 status.
        print 'Status: 404 Not Found'
        print doc.Format()
        syslog('error', 'edithtml: No such list "%s": %s', listname, e)
        return
Пример #24
0
def show_pending_unsubs(mlist, form):
    # Add the pending unsubscription request section
    lang = mlist.preferred_language
    pendingunsubs = mlist.GetUnsubscriptionIds()
    if not pendingunsubs:
        return 0
    table = Table(border=2)
    table.AddRow([Center(Bold(_('User address/name'))),
                  Center(Bold(_('Your decision'))),
                  Center(Bold(_('Reason for refusal')))
                  ])
    # Alphabetical order by email address
    byaddrs = {}
    for id in pendingunsubs:
        addr = mlist.GetRecord(id)
        byaddrs.setdefault(addr, []).append(id)
    addrs = byaddrs.items()
    addrs.sort()
    num = 0
    for addr, ids in addrs:
        # Eliminate duplicates
        # Here the order doesn't matter as the data is just the address.
        for id in ids[1:]:
            mlist.HandleRequest(id, mm_cfg.DISCARD)
        id = ids[0]
        addr = mlist.GetRecord(id)
        try:
            fullname = Utils.uncanonstr(mlist.getMemberName(addr), lang)
        except Errors.NotAMemberError:
            # They must have been unsubscribed elsewhere, so we can just
            # discard this record.
            mlist.HandleRequest(id, mm_cfg.DISCARD)
            continue
        num += 1
        table.AddRow(['%s<br><em>%s</em>' % (addr, Utils.websafe(fullname)),
                      RadioButtonArray(id, (_('Defer'),
                                            _('Approve'),
                                            _('Reject'),
                                            _('Discard')),
                                       values=(mm_cfg.DEFER,
                                               mm_cfg.UNSUBSCRIBE,
                                               mm_cfg.REJECT,
                                               mm_cfg.DISCARD),
                                       checked=0),
                      TextBox('comment-%d' % id, size=45)
                      ])
    if num > 0:
        form.AddItem('<hr>')
        form.AddItem(Center(Header(2, _('Unsubscription Requests'))))
        form.AddItem(table)
    return num
Пример #25
0
def main():
    parts = Utils.GetPathPieces()
    if not parts:
        listinfo_overview()
        return

    listname = parts[0].lower()
    try:
        mlist = MailList.MailList(listname, lock=0)
    except Errors.MMListError, e:
        # Avoid cross-site scripting attacks
        safelistname = Utils.websafe(listname)
        listinfo_overview(_('No such list <em>%(safelistname)s</em>'))
        syslog('error', 'No such list "%s": %s', listname, e)
        return
Пример #26
0
def main():
    parts = Utils.GetPathPieces()
    if not parts:
        error_page(_('Invalid options to CGI script'))
        return

    listname = parts[0].lower()
    try:
        mlist = MailList.MailList(listname, lock=0)
    except Errors.MMListError, e:
        # Avoid cross-site scripting attacks
        safelistname = Utils.websafe(listname)
        error_page(_('No such list <em>%(safelistname)s</em>'))
        syslog('error', 'roster: no such list "%s": %s', listname, e)
        return
Пример #27
0
 def __init__ (self):
     HTMLAction.__init__(self, "ctl-listadmin.html")
     self._ln = self.cgival('lc_name').lower()
     self._priv = self.cgival('lc_private') != ''
     self._safelin = Utils.websafe(self.ln)
     self._pw = mm_cfg.SSO_STOCK_ADMIN_PWD
     self._owner = self.get_owners()
     self._hn = Utils.get_domain()
     self._eh = mm_cfg.VIRTUAL_HOSTS.get(self.hn, mm_cfg.DEFAULT_EMAIL_HOST)
     self._ml = None
     self._langs = [mm_cfg.DEFAULT_SERVER_LANGUAGE]
     self._moderate = mm_cfg.DEFAULT_DEFAULT_MEMBER_MODERATION
     self._notify = 1
     self._info = self.cgival('lc_info')
     self._welcome = self.cgival('lc_welcome')
     self._desc = self.cgival('lc_desc')
Пример #28
0
def main():
    # Figure out which list is being requested
    parts = Utils.GetPathPieces()
    if not parts:
        handle_no_list()
        return

    listname = parts[0].lower()
    try:
        mlist = MailList.MailList(listname, lock=0)
    except Errors.MMListError, e:
        # Avoid cross-site scripting attacks
        safelistname = Utils.websafe(listname)
        handle_no_list(_('No such list <em>%(safelistname)s</em>'))
        syslog('error', 'No such list "%s": %s\n', listname, e)
        return
Пример #29
0
def main():
    doc = Document()
    doc.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE)

    cgidata = cgi.FieldStorage()
    try:
        cgidata.getfirst('password', '')
    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

    parts = Utils.GetPathPieces()

    if not parts:
        # Bad URL specification
        title = _('Bad URL specification')
        doc.SetTitle(title)
        doc.AddItem(
            Header(3, Bold(FontAttr(title, color='#ff0000', size='+2'))))
        doc.AddItem('<hr>')
        doc.AddItem(MailmanLogo())
        print doc.Format()
        syslog('error', 'Bad URL specification: %s', parts)
        return

    listname = parts[0].lower()
    try:
        mlist = MailList.MailList(listname, lock=0)
    except Errors.MMListError, e:
        # Avoid cross-site scripting attacks
        safelistname = Utils.websafe(listname)
        title = _('No such list <em>%(safelistname)s</em>')
        doc.SetTitle(_('No such list %(safelistname)s'))
        doc.AddItem(
            Header(3, Bold(FontAttr(title, color='#ff0000', size='+2'))))
        doc.AddItem('<hr>')
        doc.AddItem(MailmanLogo())
        # Send this with a 404 status.
        print 'Status: 404 Not Found'
        print doc.Format()
        syslog('error', 'rmlist: No such list "%s": %s\n', listname, e)
        return
Пример #30
0
def main():
    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, 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
Пример #31
0
def main():
    parts = Utils.GetPathPieces()
    if not parts:
        listinfo_overview()
        return

    listname = parts[0].lower()
    try:
        mlist = MailList.MailList(listname, lock=0)
    except Errors.MMListError, e:
        # Avoid cross-site scripting attacks
        safelistname = Utils.websafe(listname)
        # Send this with a 404 status.
        print 'Status: 404 Not Found'
        listinfo_overview(_('No such list <em>%(safelistname)s</em>'))
        syslog('error', 'No such list "%s": %s', listname, e)
        return
Пример #32
0
 def __init__(self,
              name,
              text='',
              rows=None,
              cols=None,
              wrap='soft',
              readonly=0):
     if isinstance(text, str):
         safetext = Utils.websafe(text)
     else:
         safetext = text
     self.name = name
     self.text = safetext
     self.rows = rows
     self.cols = cols
     self.wrap = wrap
     self.readonly = readonly
Пример #33
0
def main():
    # Figure out which list is being requested
    parts = Utils.GetPathPieces()
    if not parts:
        handle_no_list()
        return

    listname = parts[0].lower()
    try:
        mlist = MailList.MailList(listname, lock=0)
    except Errors.MMListError, e:
        # Avoid cross-site scripting attacks
        safelistname = Utils.websafe(listname)
        # Send this with a 404 status.
        print 'Status: 404 Not Found'
        handle_no_list(_('No such list <em>%(safelistname)s</em>'))
        syslog('error', 'No such list "%s": %s\n', listname, e)
        return
Пример #34
0
def main():
    global ssort
    # Figure out which list is being requested
    parts = Utils.GetPathPieces()
    if not parts:
        handle_no_list()
        return

    listname = parts[0].lower()
    try:
        mlist = MailList.MailList(listname, lock=0)
    except Errors.MMListError, e:
        # Avoid cross-site scripting attacks
        safelistname = Utils.websafe(listname)
        # Send this with a 404 status.
        print 'Status: 404 Not Found'
        handle_no_list(_('No such list <em>%(safelistname)s</em>'))
        syslog('error', 'admindb: No such list "%s": %s\n', listname, e)
        return
Пример #35
0
 def FormatUsers(self, digest, lang=None, list_hidden=False):
     if lang is None:
         lang = self.preferred_language
     conceal_sub = mm_cfg.ConcealSubscription
     people = []
     if digest:
         members = self.getDigestMemberKeys()
     else:
         members = self.getRegularMemberKeys()
     for m in members:
         if list_hidden or not self.getMemberOption(m, conceal_sub):
             people.append(m)
     num_concealed = len(members) - len(people)
     if num_concealed == 1:
         concealed = _('<em>(1 private member not shown)</em>')
     elif num_concealed > 1:
         concealed = _(
             '<em>(%(num_concealed)d private members not shown)</em>')
     else:
         concealed = ''
     items = []
     people.sort()
     obscure = self.obscure_addresses
     for person in people:
         id = Utils.ObscureEmail(person)
         url = self.GetOptionsURL(person, obscure=obscure)
         person = self.getMemberCPAddress(person)
         if obscure:
             showing = Utils.ObscureEmail(person, for_text=1)
         else:
             showing = person
         realname = Utils.uncanonstr(self.getMemberName(person), lang)
         if realname and mm_cfg.ROSTER_DISPLAY_REALNAME:
             showing += " (%s)" % Utils.websafe(realname)
         got = Link(url, showing)
         if self.getDeliveryStatus(person) <> MemberAdaptor.ENABLED:
             got = Italic('(', got, ')')
         items.append(got)
     # Just return the .Format() so this works until I finish
     # converting everything to htmlformat...
     return concealed + UnorderedList(*tuple(items)).Format()
 def FormatUsers(self, digest, lang=None, list_hidden=False):
     if lang is None:
         lang = self.preferred_language
     conceal_sub = mm_cfg.ConcealSubscription
     people = []
     if digest:
         members = self.getDigestMemberKeys()
     else:
         members = self.getRegularMemberKeys()
     for m in members:
         if list_hidden or not self.getMemberOption(m, conceal_sub):
             people.append(m)
     num_concealed = len(members) - len(people)
     if num_concealed == 1:
         concealed = _('<em>(1 private member not shown)</em>')
     elif num_concealed > 1:
        concealed = _(
            '<em>(%(num_concealed)d private members not shown)</em>')
     else:
         concealed = ''
     items = []
     people.sort()
     obscure = self.obscure_addresses
     for person in people:
         id = Utils.ObscureEmail(person)
         url = self.GetOptionsURL(person, obscure=obscure)
         if obscure:
             showing = Utils.ObscureEmail(person, for_text=1)
         else:
             showing = person
         realname = Utils.uncanonstr(self.getMemberName(person), lang)
         if realname and mm_cfg.ROSTER_DISPLAY_REALNAME:
             showing += " (%s)" % Utils.websafe(realname)
         got = Link(url, showing)
         if self.getDeliveryStatus(person) <> MemberAdaptor.ENABLED:
             got = Italic('(', got, ')')
         items.append(got)
     # Just return the .Format() so this works until I finish
     # converting everything to htmlformat...
     return concealed + UnorderedList(*tuple(items)).Format()
Пример #37
0
def main():
    doc = Document()
    doc.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE)

    parts = Utils.GetPathPieces()
    if not parts:
        doc.AddItem(Header(2, _("Error")))
        doc.AddItem(Bold(_('Invalid options to CGI script')))
        print doc.Format()
        return

    listname = parts[0].lower()
    try:
        mlist = MailList.MailList(listname, lock=0)
    except Errors.MMListError, e:
        # Avoid cross-site scripting attacks
        safelistname = Utils.websafe(listname)
        doc.AddItem(Header(2, _("Error")))
        doc.AddItem(Bold(_('No such list <em>%(safelistname)s</em>')))
        print doc.Format()
        syslog('error', 'No such list "%s": %s\n', listname, e)
        return
Пример #38
0
def main():
    # Trick out pygettext since we want to mark template_data as translatable,
    # but we don't want to actually translate it here.
    def _(s):
        return s

    template_data = (
        ('listinfo.html',    _('General list information page')),
        ('subscribe.html',   _('Subscribe results page')),
        ('options.html',     _('User specific options page')),
        ('subscribeack.txt', _('Welcome email text file')),
        ('masthead.txt',     _('Digest masthead')),
        )

    _ = i18n._
    doc = Document()

    # Set up the system default language
    i18n.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE)
    doc.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE)

    parts = Utils.GetPathPieces()
    if not parts:
        doc.AddItem(Header(2, _("List name is required.")))
        print doc.Format()
        return

    listname = parts[0].lower()
    try:
        mlist = MailList.MailList(listname, lock=0)
    except Errors.MMListError, e:
        # Avoid cross-site scripting attacks
        safelistname = Utils.websafe(listname)
        doc.AddItem(Header(2, _('No such list <em>%(safelistname)s</em>')))
        # Send this with a 404 status.
        print 'Status: 404 Not Found'
        print doc.Format()
        syslog('error', 'No such list "%s": %s', listname, e)
        return
Пример #39
0
def main():
    # print 'Content-type: text/plain\n\n'
    print 'Content-type: text/plain; charset=us-ascii\n'

    doc = Document()
    doc.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE)

    parts = Utils.GetPathPieces()
    if not parts:
        doc.SetTitle(_("Private Archive Error"))
        doc.AddItem(Header(3, _("You must specify a list.")))
        print doc.Format()
        return

    path = os.environ.get('PATH_INFO')
    tpath = true_path(path)
    if tpath <> path[1:]:
        msg = _('Private archive - "./" and "../" not allowed in URL.')
        doc.SetTitle(msg)
        doc.AddItem(Header(2, msg))
        print doc.Format()
        syslog('mischief', 'Private archive hostile path: %s', path)
        return
    # BAW: This needs to be converted to the Site module abstraction
    true_filename = os.path.join(mm_cfg.PRIVATE_ARCHIVE_FILE_DIR, tpath)

    listname = parts[0].lower()

    try:
        mlist = MailList.MailList(listname, lock=0)
    except Errors.MMListError, e:
        # Avoid cross-site scripting attacks
        safelistname = Utils.websafe(listname)
        msg = _('No such list <em>%(safelistname)s</em>')
        doc.SetTitle(_("Private Archive Error - %(msg)s"))
        doc.AddItem(Header(2, msg))
        print doc.Format()
        syslog('error', 'No such list "%s": %s\n', listname, e)
        return
Пример #40
0
 def handleForm(self, mlist, category, subcat, cgidata, doc):
     for item in self.GetConfigInfo(mlist, category, subcat):
         # Skip descriptions and legacy non-attributes
         if not isinstance(item, tuple) or len(item) < 5:
             continue
         # Unpack the gui item description
         property, wtype, args, deps, desc = item[0:5]
         # BAW: I know this code is a little crufty but I wanted to
         # reproduce the semantics of the original code in admin.py as
         # closely as possible, for now.  We can clean it up later.
         #
         # The property may be uploadable...
         uploadprop = property + '_upload'
         if uploadprop in cgidata and cgidata[uploadprop].value:
             val = cgidata[uploadprop].value
         elif property not in cgidata:
             continue
         elif isinstance(cgidata[property], list):
             val = [x.value for x in cgidata[property]]
         else:
             val = cgidata[property].value
         # Coerce the value to the expected type, raising exceptions if the
         # value is invalid.
         try:
             val = self._getValidValue(mlist, property, wtype, val)
         except ValueError:
             doc.addError(_('Invalid value for variable: %(property)s'))
         # This is the parent of MMBadEmailError and MMHostileAddress
         except Errors.EmailAddressError as error:
             error = Utils.websafe(str(error))
             doc.addError(
                 _('Bad email address for option %(property)s: %(error)s'))
         else:
             # Set the attribute, which will normally delegate to the mlist
             self._setValue(mlist, property, val, doc)
     # Do a final sweep once all the attributes have been set.  This is how
     # we can do cross-attribute assertions
     self._postValidate(mlist, doc)
Пример #41
0
def main():
    parts = Utils.GetPathPieces()
    if not parts:
        listinfo_overview()
        return

    listname = parts[0].lower()
    try:
        mlist = MailList.MailList(listname, lock=0)
    except Errors.MMListError as e:
        # Avoid cross-site scripting attacks
        safelistname = Utils.websafe(listname)
        # Send this with a 404 status.
        print('Status: 404 Not Found')
        listinfo_overview(_('No such list <em>%(safelistname)s</em>'))
        syslog('error', 'listinfo: No such list "%s": %s', listname, e)
        return

    # See if the user want to see this page in other language
    cgidata = cgi.FieldStorage()
    try:
        language = cgidata.getfirst('language')
    except TypeError:
        # Someone crafted a POST with a bad Content-Type:.
        doc = Document()
        doc.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE)
        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 not Utils.IsLanguage(language):
        language = mlist.preferred_language
    i18n.set_language(language)
    list_listinfo(mlist, language)
Пример #42
0
    def handler (self, parts):
        owned = []
        cu = self.curr_user
        for mln, ml in self.all_mls.iteritems():
            if cu in ml.owner or cu in mm_cfg.SSO_ADMIN_AUTHIDS:
                owned.append({'real_name' : ml.real_name,
                              'description' : Utils.websafe(ml.description),
                              'advertised' : ml.advertised,
                              })
        self.kwargs_add('lists', owned)
        self.kwargs_add('can_create_lists', can_create_lists)

        if self.cgidata.has_key('lc_submit'):
            if parts[0] == 'new':
                self.handler_new(parts, submit=True)
            else:
                self.handler_edit(parts)
        else:
            ## This is the case when we are landing here through a GET
            ## request.
            self.kwargs_add('action_taken', False)

            if (len(parts) == 0 or parts[0] == ''):
                ## This is the root /create page
                self.kwargs_add('list_to_edit', None)
                self.kwargs_add('lc_empty_form', False)
            elif parts[0] == 'new':
                ## This is the root /create/new page
                self.handler_new(parts, submit=False)
            else:
                ## This is some /create/xyz type page which is for editing the
                ## configuration of list named xyz
                self.kwargs_add('list_to_edit', self.all_mls[parts[0].lower()])
                self.kwargs_add('lc_empty_form', False)

        self.render()
Пример #43
0
def main():
    doc = Document()
    doc.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE)

    cgidata = cgi.FieldStorage()
    parts = Utils.GetPathPieces()

    if not parts:
        # Bad URL specification
        title = _('Bad URL specification')
        doc.SetTitle(title)
        doc.AddItem(
            Header(3, Bold(FontAttr(title, color='#ff0000', size='+2'))))
        doc.AddItem('<hr>')
        doc.AddItem(MailmanLogo())
        print doc.Format()
        syslog('error', 'Bad URL specification: %s', parts)
        return

    listname = parts[0].lower()
    try:
        mlist = MailList.MailList(listname, lock=0)
    except Errors.MMListError, e:
        # Avoid cross-site scripting attacks
        safelistname = Utils.websafe(listname)
        title = _('No such list <em>%(safelistname)s</em>')
        doc.SetTitle(title)
        doc.AddItem(
            Header(3, Bold(FontAttr(title, color='#ff0000', size='+2'))))
        doc.AddItem('<hr>')
        doc.AddItem(MailmanLogo())
        # Send this with a 404 status.
        print 'Status: 404 Not Found'
        print doc.Format()
        syslog('error', 'No such list "%s": %s\n', listname, e)
        return
Пример #44
0
def main():
    doc = Document()
    doc.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE)

    parts = Utils.GetPathPieces()
    if not parts:
        doc.AddItem(Header(2, _("Error")))
        doc.AddItem(Bold(_('Invalid options to CGI script')))
        print doc.Format()
        return

    listname = parts[0].lower()
    try:
        mlist = MailList.MailList(listname, lock=0)
    except Errors.MMListError, e:
        # Avoid cross-site scripting attacks
        safelistname = Utils.websafe(listname)
        doc.AddItem(Header(2, _("Error")))
        doc.AddItem(Bold(_('No such list <em>%(safelistname)s</em>')))
        # Send this with a 404 status.
        print 'Status: 404 Not Found'
        print doc.Format()
        syslog('error', 'subscribe: No such list "%s": %s\n', listname, e)
        return
Пример #45
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
Пример #46
0
def CGIescape(arg, lang=None):
    if isinstance(arg, types.UnicodeType):
        s = Utils.websafe(arg)
    else:
        s = Utils.websafe(str(arg))
    return Utils.uncanonstr(s.replace('"', '&quot;'), lang)
Пример #47
0
def show_helds_overview(mlist, form):
    # Sort the held messages by sender
    bysender = helds_by_sender(mlist)
    if not bysender:
        return 0
    form.AddItem('<hr>')
    form.AddItem(Center(Header(2, _('Held Messages'))))
    # Add the by-sender overview tables
    admindburl = mlist.GetScriptURL('admindb', absolute=1)
    table = Table(border=0)
    form.AddItem(table)
    senders = bysender.keys()
    senders.sort()
    for sender in senders:
        qsender = quote_plus(sender)
        esender = Utils.websafe(sender)
        senderurl = admindburl + '?sender=' + qsender
        # The encompassing sender table
        stable = Table(border=1)
        stable.AddRow([Center(Bold(_('From:')).Format() + esender)])
        stable.AddCellInfo(stable.GetCurrentRowIndex(), 0, colspan=2)
        left = Table(border=0)
        left.AddRow([_('Action to take on all these held messages:')])
        left.AddCellInfo(left.GetCurrentRowIndex(), 0, colspan=2)
        btns = hacky_radio_buttons(
            'senderaction-' + qsender,
            (_('Defer'), _('Accept'), _('Reject'), _('Discard')),
            (mm_cfg.DEFER, mm_cfg.APPROVE, mm_cfg.REJECT, mm_cfg.DISCARD),
            (1, 0, 0, 0))
        left.AddRow([btns])
        left.AddCellInfo(left.GetCurrentRowIndex(), 0, colspan=2)
        left.AddRow([
            CheckBox('senderpreserve-' + qsender, 1).Format() +
            '&nbsp;' +
            _('Preserve messages for the site administrator')
            ])
        left.AddCellInfo(left.GetCurrentRowIndex(), 0, colspan=2)
        left.AddRow([
            CheckBox('senderforward-' + qsender, 1).Format() +
            '&nbsp;' +
            _('Forward messages (individually) to:')
            ])
        left.AddCellInfo(left.GetCurrentRowIndex(), 0, colspan=2)
        left.AddRow([
            TextBox('senderforwardto-' + qsender,
                    value=mlist.GetOwnerEmail())
            ])
        left.AddCellInfo(left.GetCurrentRowIndex(), 0, colspan=2)
        # If the sender is a member and the message is being held due to a
        # moderation bit, give the admin a chance to clear the member's mod
        # bit.  If this sender is not a member and is not already on one of
        # the sender filters, then give the admin a chance to add this sender
        # to one of the filters.
        if mlist.isMember(sender):
            if mlist.getMemberOption(sender, mm_cfg.Moderate):
                left.AddRow([
                    CheckBox('senderclearmodp-' + qsender, 1).Format() +
                    '&nbsp;' +
                    _("Clear this member's <em>moderate</em> flag")
                    ])
            else:
                left.AddRow(
                    [_('<em>The sender is now a member of this list</em>')])
            left.AddCellInfo(left.GetCurrentRowIndex(), 0, colspan=2)
        elif sender not in (mlist.accept_these_nonmembers +
                            mlist.hold_these_nonmembers +
                            mlist.reject_these_nonmembers +
                            mlist.discard_these_nonmembers):
            left.AddRow([
                CheckBox('senderfilterp-' + qsender, 1).Format() +
                '&nbsp;' +
                _('Add <b>%(esender)s</b> to one of these sender filters:')
                ])
            left.AddCellInfo(left.GetCurrentRowIndex(), 0, colspan=2)
            btns = hacky_radio_buttons(
                'senderfilter-' + qsender,
                (_('Accepts'), _('Holds'), _('Rejects'), _('Discards')),
                (mm_cfg.ACCEPT, mm_cfg.HOLD, mm_cfg.REJECT, mm_cfg.DISCARD),
                (0, 0, 0, 1))
            left.AddRow([btns])
            left.AddCellInfo(left.GetCurrentRowIndex(), 0, colspan=2)
            if sender not in mlist.ban_list:
                left.AddRow([
                    CheckBox('senderbanp-' + qsender, 1).Format() +
                    '&nbsp;' +
                    _("""Ban <b>%(esender)s</b> from ever subscribing to this
                    mailing list""")])
                left.AddCellInfo(left.GetCurrentRowIndex(), 0, colspan=2)
        right = Table(border=0)
        right.AddRow([
            _("""Click on the message number to view the individual
            message, or you can """) +
            Link(senderurl, _('view all messages from %(esender)s')).Format()
            ])
        right.AddCellInfo(right.GetCurrentRowIndex(), 0, colspan=2)
        right.AddRow(['&nbsp;', '&nbsp;'])
        counter = 1
        for id in bysender[sender]:
            info = mlist.GetRecord(id)
            ptime, sender, subject, reason, filename, msgdata = info
            # BAW: This is really the size of the message pickle, which should
            # be close, but won't be exact.  Sigh, good enough.
            try:
                size = os.path.getsize(os.path.join(mm_cfg.DATA_DIR, filename))
            except OSError, e:
                if e.errno <> errno.ENOENT: raise
                # This message must have gotten lost, i.e. it's already been
                # handled by the time we got here.
                mlist.HandleRequest(id, mm_cfg.DISCARD)
                continue
            dispsubj = Utils.oneline(
                subject, Utils.GetCharSet(mlist.preferred_language))
            t = Table(border=0)
            t.AddRow([Link(admindburl + '?msgid=%d' % id, '[%d]' % counter),
                      Bold(_('Subject:')),
                      Utils.websafe(dispsubj)
                      ])
            t.AddRow(['&nbsp;', Bold(_('Size:')), str(size) + _(' bytes')])
            if reason:
                reason = _(reason)
            else:
                reason = _('not available')
            t.AddRow(['&nbsp;', Bold(_('Reason:')), reason])
            # Include the date we received the message, if available
            when = msgdata.get('received_time')
            if when:
                t.AddRow(['&nbsp;', Bold(_('Received:')),
                          time.ctime(when)])
            t.AddRow([InputObj(qsender, 'hidden', str(id), False).Format()])
            counter += 1
            right.AddRow([t])
        stable.AddRow([left, right])
        table.AddRow([stable])
Пример #48
0
         CheckBox('discardalldefersp', 0).Format() +
         '&nbsp;' +
         _('Discard all messages marked <em>Defer</em>')
         ))
 # Add a link back to the overview, if we're not viewing the overview!
 adminurl = mlist.GetScriptURL('admin', absolute=1)
 d = {'listname'  : mlist.real_name,
      'detailsurl': admindburl + '?details=instructions',
      'summaryurl': admindburl,
      'viewallurl': admindburl + '?details=all',
      'adminurl'  : adminurl,
      'filterurl' : adminurl + '/privacy/sender',
      }
 addform = 1
 if sender:
     esender = Utils.websafe(sender)
     d['description'] = _("all of %(esender)s's held messages.")
     doc.AddItem(Utils.maketext('admindbpreamble.html', d,
                                raw=1, mlist=mlist))
     show_sender_requests(mlist, form, sender)
 elif msgid:
     d['description'] = _('a single held message.')
     doc.AddItem(Utils.maketext('admindbpreamble.html', d,
                                raw=1, mlist=mlist))
     show_message_requests(mlist, form, msgid)
 elif details == 'all':
     d['description'] = _('all held messages.')
     doc.AddItem(Utils.maketext('admindbpreamble.html', d,
                                raw=1, mlist=mlist))
     show_detailed_requests(mlist, form)
 elif details == 'instructions':
Пример #49
0
def main():
    # Trick out pygettext since we want to mark template_data as translatable,
    # but we don't want to actually translate it here.
    def _(s):
        return s

    template_data = (
        ('listinfo.html', _('General list information page')),
        ('subscribe.html', _('Subscribe results page')),
        ('options.html', _('User specific options page')),
        ('subscribeack.txt', _('Welcome email text file')),
        ('masthead.txt', _('Digest masthead')),
        ('postheld.txt', _('User notice of held post')),
        ('approve.txt', _('User notice of held subscription')),
        ('refuse.txt', _('Notice of post refused by moderator')),
        ('invite.txt', _('Invitation to join list')),
        ('verify.txt', _('Request to confirm subscription')),
        ('unsub.txt', _('Request to confirm unsubscription')),
        ('nomoretoday.txt', _('User notice of autoresponse limit')),
        ('postack.txt', _('User post acknowledgement')),
        ('disabled.txt', _('Subscription disabled by bounce warning')),
        ('admlogin.html', _('Admin/moderator login page')),
        ('private.html', _('Private archive login page')),
        ('userpass.txt', _('On demand password reminder')),
    )

    _ = i18n._
    doc = Document()

    # Set up the system default language
    i18n.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE)
    doc.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE)

    parts = Utils.GetPathPieces()
    if not parts:
        doc.AddItem(Header(2, _("List name is required.")))
        print(doc.Format())
        return

    listname = parts[0].lower()
    try:
        mlist = MailList.MailList(listname, lock=0)
    except Errors.MMListError as e:
        # Avoid cross-site scripting attacks
        safelistname = Utils.websafe(listname)
        doc.AddItem(Header(2, _('No such list <em>%(safelistname)s</em>')))
        # Send this with a 404 status.
        print('Status: 404 Not Found')
        print(doc.Format())
        syslog('error', 'edithtml: No such list "%s": %s', listname, e)
        return

    # Now that we have a valid list, set the language to its default
    i18n.set_language(mlist.preferred_language)
    doc.set_language(mlist.preferred_language)

    # Must be authenticated to get any farther
    cgidata = cgi.FieldStorage()
    try:
        cgidata.getfirst('adminpw', '')
    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

    # CSRF check
    safe_params = ['VARHELP', 'adminpw', 'admlogin']
    params = list(cgidata.keys())
    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('adminpw'):
        os.environ['HTTP_COOKIE'] = ''
        csrf_checked = True

    # Editing the html for a list is limited to the list admin and site admin.
    if not mlist.WebAuthenticate((mm_cfg.AuthListAdmin, mm_cfg.AuthSiteAdmin),
                                 cgidata.getfirst('adminpw', '')):
        if 'admlogin' in cgidata:
            # This is a re-authorization attempt
            msg = Bold(FontSize('+1', _('Authorization failed.'))).Format()
            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 (edithtml): list=%s: remote=%s',
                   listname, remote)
        else:
            msg = ''
        Auth.loginpage(mlist, 'admin', msg=msg)
        return

    realname = mlist.real_name
    if len(parts) > 1:
        template_name = parts[1]
        for (template, info) in template_data:
            if template == template_name:
                template_info = _(info)
                doc.SetTitle(
                    _('%(realname)s -- Edit html for %(template_info)s'))
                break
        else:
            # Avoid cross-site scripting attacks
            safetemplatename = Utils.websafe(template_name)
            doc.SetTitle(_('Edit HTML : Error'))
            doc.AddItem(Header(2, _("%(safetemplatename)s: Invalid template")))
            doc.AddItem(mlist.GetMailmanFooter())
            print(doc.Format())
            return
    else:
        doc.SetTitle(_('%(realname)s -- HTML Page Editing'))
        doc.AddItem(Header(1, _('%(realname)s -- HTML Page Editing')))
        doc.AddItem(Header(2, _('Select page to edit:')))
        template_list = UnorderedList()
        for (template, info) in template_data:
            l = Link(mlist.GetScriptURL('edithtml') + '/' + template, _(info))
            template_list.AddItem(l)
        doc.AddItem(FontSize("+2", template_list))
        doc.AddItem(mlist.GetMailmanFooter())
        print(doc.Format())
        return

    try:
        if list(cgidata.keys()):
            if csrf_checked:
                ChangeHTML(mlist, cgidata, template_name, doc)
            else:
                doc.addError(
                    _('The form lifetime has expired. (request forgery check)')
                )
        FormatHTML(mlist, doc, template_name, template_info)
    finally:
        doc.AddItem(mlist.GetMailmanFooter())
        print(doc.Format())
Пример #50
0
def process(mlist, msg, msgdata=None):
    sanitize = mm_cfg.ARCHIVE_HTML_SANITIZER
    outer = True
    in_pipeline = False
    patches = None
    sigs = None
    if msgdata is None:
        msgdata = {}
    if msgdata:
        # msgdata is available if it is in GLOBAL_PIPELINE
        # ie. not in digest or archiver
        # check if the list owner want to scrub regular delivery
        # Disabled - function split, attachments saved in pipeline,
        # if not mlist.scrub_nondigest:
        #    return
        in_pipeline = True
        patches = []
        sigs = []

    dir = calculate_attachments_dir(mlist, msg, msgdata)
    charset = None
    lcset = Utils.GetCharSet(mlist.preferred_language)
    lcset_out = Charset(lcset).output_charset or lcset
    # Now walk over all subparts of this message and scrub out various types
    format = delsp = None
    for part in msg.walk():
        ctype = part.get_content_type()
        # If the part is text/plain, we leave it alone
        if ctype == "text/plain":
            # We need to choose a charset for the scrubbed message, so we'll
            # arbitrarily pick the charset of the first text/plain part in the
            # message.
            # MAS: Also get the RFC 3676 stuff from this part. This seems to
            # work OK for scrub_nondigest.  It will also work as far as
            # scrubbing messages for the archive is concerned, but pipermail
            # doesn't pay any attention to the RFC 3676 parameters.  The plain
            # format digest is going to be a disaster in any case as some of
            # messages will be format="flowed" and some not.  ToDigest creates
            # its own Content-Type: header for the plain digest which won't
            # have RFC 3676 parameters. If the message Content-Type: headers
            # are retained for display in the digest, the parameters will be
            # there for information, but not for the MUA. This is the best we
            # can do without having get_payload() process the parameters.
            if charset is None:
                charset = part.get_content_charset(lcset)
                format = part.get_param("format")
                delsp = part.get_param("delsp")
            # TK: if part is attached then check charset and scrub if none
            if part.get("content-disposition") and not part.get_content_charset():
                if in_pipeline:
                    omask = os.umask(002)
                    try:
                        url = save_attachment(mlist, part, dir, patches=patches, sigs=sigs)
                        part[mm_cfg.SCRUBBER_URL_HEADER] = url
                    finally:
                        os.umask(omask)
                else:
                    url = "<" + part.get(mm_cfg.SCRUBBER_URL_HEADER, "N/A") + ">"
                    filename = part.get_filename(_("not available"))
                    filename = Utils.oneline(filename, lcset)
                    replace_payload_by_text(
                        part,
                        _(
                            """\
An embedded and charset-unspecified text was scrubbed...
Name: %(filename)s
URL: %(url)s
"""
                        ),
                        lcset,
                    )
            elif mm_cfg.SCRUBBER_ARCHIVE_ALL_TEXT:
                if in_pipeline:
                    # clearsigned or attached plaintext that will be shown, still archive the copy
                    omask = os.umask(002)
                    try:
                        url = save_attachment(mlist, part, dir, patches=patches, sigs=sigs)
                        part[mm_cfg.SCRUBBER_URL_HEADER] = url
                    finally:
                        os.umask(omask)
                elif outer and not msg.is_multipart():
                    # whole email is only one plaintext. add URL here
                    payload = msg.get_payload(decode=True)
                    del msg["content-type"]
                    del msg["content-transfer-encoding"]
                    payload = "URL: <" + part.get(mm_cfg.SCRUBBER_URL_HEADER, "N/A") + ">\n" + payload
                    msg.set_payload(payload)

        elif ctype == "text/html" and isinstance(sanitize, IntType):
            if sanitize == 0:
                if outer:
                    raise DiscardMessage
                replace_payload_by_text(
                    part,
                    _("HTML attachment scrubbed and removed"),
                    # Adding charset arg and removing content-type
                    # sets content-type to text/plain
                    lcset,
                )
            elif sanitize == 2:
                # By leaving it alone, Pipermail will automatically escape it
                pass
            elif sanitize == 3:
                if in_pipeline:
                    # Pull it out as an attachment but leave it unescaped.  This
                    # is dangerous, but perhaps useful for heavily moderated
                    # lists.
                    omask = os.umask(002)
                    try:
                        url = save_attachment(mlist, part, dir, filter_html=False, patches=patches, sigs=sigs)
                        part[mm_cfg.SCRUBBER_URL_HEADER] = url
                    finally:
                        os.umask(omask)
                else:
                    url = "<" + part.get(mm_cfg.SCRUBBER_URL_HEADER, "N/A") + ">"
                    replace_payload_by_text(
                        part,
                        _(
                            """\
An HTML attachment was scrubbed...
URL: %(url)s
"""
                        ),
                        lcset,
                    )
            else:
                if in_pipeline:
                    # TODO if in_pipeline should preserve original attachment. but no biggie
                    # HTML-escape it and store it as an attachment, but make it
                    # look a /little/ bit prettier. :(
                    payload = Utils.websafe(part.get_payload(decode=True))
                    # For whitespace in the margin, change spaces into
                    # non-breaking spaces, and tabs into 8 of those.  Then use a
                    # mono-space font.  Still looks hideous to me, but then I'd
                    # just as soon discard them.
                    def doreplace(s):
                        return s.expandtabs(8).replace(" ", "&nbsp;")

                    lines = [doreplace(s) for s in payload.split("\n")]
                    payload = "<tt>\n" + BR.join(lines) + "\n</tt>\n"
                    part.set_payload(payload)
                    # We're replacing the payload with the decoded payload so this
                    # will just get in the way.
                    del part["content-transfer-encoding"]
                    omask = os.umask(002)
                    try:
                        url = save_attachment(mlist, part, dir, filter_html=False, patches=patches, sigs=sigs)
                        part[mm_cfg.SCRUBBER_URL_HEADER] = url
                    finally:
                        os.umask(omask)
                else:
                    url = "<" + part.get(mm_cfg.SCRUBBER_URL_HEADER, "N/A") + ">"
                    replace_payload_by_text(
                        part,
                        _(
                            """\
An HTML attachment was scrubbed...
URL: %(url)s
"""
                        ),
                        lcset,
                    )
        elif ctype == "message/rfc822":
            if in_pipeline:
                omask = os.umask(002)
                try:
                    url = save_attachment(mlist, part, dir)
                    part[mm_cfg.SCRUBBER_URL_HEADER] = url
                finally:
                    os.umask(omask)
            else:
                # This part contains a submessage, so it too needs scrubbing
                submsg = part.get_payload(0)
                url = "<" + part.get(mm_cfg.SCRUBBER_URL_HEADER, "N/A") + ">"
                subject = submsg.get("subject", _("no subject"))
                subject = Utils.oneline(subject, lcset)
                date = submsg.get("date", _("no date"))
                who = submsg.get("from", _("unknown sender"))
                size = len(str(submsg))
                replace_payload_by_text(
                    part,
                    _(
                        """\
An embedded message was scrubbed...
From: %(who)s
Subject: %(subject)s
Date: %(date)s
Size: %(size)s
URL: %(url)s
"""
                    ),
                    lcset,
                )
        # If the message isn't a multipart, then we'll strip it out as an
        # attachment that would have to be separately downloaded.  Pipermail
        # will transform the url into a hyperlink.
        elif part.get_payload() and not part.is_multipart():
            payload = part.get_payload(decode=True)
            # XXX Under email 2.5, it is possible that payload will be None.
            # This can happen when you have a Content-Type: multipart/* with
            # only one part and that part has two blank lines between the
            # first boundary and the end boundary.  In email 3.0 you end up
            # with a string in the payload.  I think in this case it's safe to
            # ignore the part.
            if payload is None or payload.strip() == "":
                continue
            if in_pipeline:
                omask = os.umask(002)
                try:
                    url = save_attachment(mlist, part, dir, patches=patches, sigs=sigs)
                    part[mm_cfg.SCRUBBER_URL_HEADER] = url
                finally:
                    os.umask(omask)
            else:
                ctype = part.get_content_type()
                size = len(payload)
                url = "<" + part.get(mm_cfg.SCRUBBER_URL_HEADER, "N/A") + ">"
                desc = part.get("content-description", _("not available"))
                desc = Utils.oneline(desc, lcset)
                filename = part.get_filename(_("not available"))
                filename = Utils.oneline(filename, lcset)
                replace_payload_by_text(
                    part,
                    _(
                        """\
A non-text attachment was scrubbed...
Name: %(filename)s
Type: %(ctype)s
Size: %(size)d bytes
Desc: %(desc)s
URL: %(url)s
"""
                    ),
                    lcset,
                )
        outer = False
    # We still have to sanitize multipart messages to flat text because
    # Pipermail can't handle messages with list payloads.  This is a kludge;
    # def (n) clever hack ;).
    if msg.is_multipart() and not in_pipeline:
        # By default we take the charset of the first text/plain part in the
        # message, but if there was none, we'll use the list's preferred
        # language's charset.
        if not charset or charset == "us-ascii":
            charset = lcset_out
        else:
            # normalize to the output charset if input/output are different
            charset = Charset(charset).output_charset or charset
        # We now want to concatenate all the parts which have been scrubbed to
        # text/plain, into a single text/plain payload.  We need to make sure
        # all the characters in the concatenated string are in the same
        # encoding, so we'll use the 'replace' key in the coercion call.
        # BAW: Martin's original patch suggested we might want to try
        # generalizing to utf-8, and that's probably a good idea (eventually).
        text = []
        for part in msg.walk():
            # TK: bug-id 1099138 and multipart
            # MAS test payload - if part may fail if there are no headers.
            if not part.get_payload() or part.is_multipart():
                continue
            # All parts should be scrubbed to text/plain by now, except
            # if sanitize == 2, there could be text/html parts so keep them
            # but skip any other parts.
            partctype = part.get_content_type()
            if partctype <> "text/plain" and (partctype <> "text/html" or sanitize <> 2):
                text.append(_("Skipped content of type %(partctype)s\n"))
                continue
            try:
                t = part.get_payload(decode=True) or ""
            # MAS: TypeError exception can occur if payload is None. This
            # was observed with a message that contained an attached
            # message/delivery-status part. Because of the special parsing
            # of this type, this resulted in a text/plain sub-part with a
            # null body. See bug 1430236.
            except (binascii.Error, TypeError):
                t = part.get_payload() or ""
            # TK: get_content_charset() returns 'iso-2022-jp' for internally
            # crafted (scrubbed) 'euc-jp' text part. So, first try
            # get_charset(), then get_content_charset() for the parts
            # which are already embeded in the incoming message.
            partcharset = part.get_charset()
            if partcharset:
                partcharset = str(partcharset)
            else:
                partcharset = part.get_content_charset()
            if partcharset and partcharset <> charset:
                try:
                    t = unicode(t, partcharset, "replace")
                except (UnicodeError, LookupError, ValueError, AssertionError):
                    # We can get here if partcharset is bogus in come way.
                    # Replace funny characters.  We use errors='replace'
                    t = unicode(t, "ascii", "replace")
                try:
                    # Should use HTML-Escape, or try generalizing to UTF-8
                    t = t.encode(charset, "replace")
                except (UnicodeError, LookupError, ValueError, AssertionError):
                    # if the message charset is bogus, use the list's.
                    t = t.encode(lcset, "replace")
            # Separation is useful
            if isinstance(t, StringType):
                # omit empty parts
                if t.strip(" \t\r\n") != "":
                    if not t.endswith("\n"):
                        t += "\n"
                    if mm_cfg.SCRUBBER_ARCHIVE_ALL_TEXT:
                        # Add link to archived part if it wasn't archived already
                        url = "URL: <" + part.get(mm_cfg.SCRUBBER_URL_HEADER, "N/A") + ">\n"
                        if not t.endswith(url):
                            t = url + t
                            filename = part.get_filename()
                            if filename:
                                filename = Utils.oneline(filename, lcset)
                                t = "Name: " + filename + "\n" + t

                    text.append(t)
        # Now join the text and set the payload
        sep = _("-------------- next part --------------\n")
        # The i18n separator is in the list's charset. Coerce it to the
        # message charset.
        try:
            s = unicode(sep, lcset, "replace")
            sep = s.encode(charset, "replace")
        except (UnicodeError, LookupError, ValueError, AssertionError):
            pass
        replace_payload_by_text(msg, sep.join(text), charset)
        if format:
            msg.set_param("Format", format)
        if delsp:
            msg.set_param("DelSp", delsp)
    if in_pipeline:
        (patches, sigs) = process_signatures(mlist, patches, sigs)
        if patches:
            # X-Patches-Received: PatchID1=filename; PatchID2=filename
            processed = {}
            for p in patches:
                processed[p["id"]] = p["file"]
            msg.add_header("X-Patches-Received", None, **processed)
        if sigs:
            # output header in RFC compliant way, if multiple sigs with multiple keys per one patch, then delimited by dot, like:
            # X-Sigs-Received: PatchID1=KeyID1; PatchID2=KeyID2.KeyID3
            processed = {}
            for s in sigs:
                processed[s["phash"]] = processed.get(s["phash"], []) + [s["key"]]
            for k in processed.keys():
                # dot does not need escaping
                processed[k] = ".".join(processed[k])
            msg.add_header("X-Sigs-Received", None, **processed)
    return msg
Пример #51
0
def options_page(mlist, doc, user, cpuser, userlang, message=''):
    # The bulk of the document will come from the options.html template, which
    # includes it's own html armor (head tags, etc.).  Suppress the head that
    # Document() derived pages get automatically.
    doc.suppress_head = 1

    if mlist.obscure_addresses:
        presentable_user = Utils.ObscureEmail(user, for_text=1)
        if cpuser is not None:
            cpuser = Utils.ObscureEmail(cpuser, for_text=1)
    else:
        presentable_user = user

    fullname = Utils.uncanonstr(mlist.getMemberName(user), userlang)
    if fullname:
        presentable_user += ', %s' % Utils.websafe(fullname)

    # Do replacements
    replacements = mlist.GetStandardReplacements(userlang)
    replacements['<mm-results>'] = Bold(FontSize('+1', message)).Format()
    replacements['<mm-digest-radio-button>'] = mlist.FormatOptionButton(
        mm_cfg.Digests, 1, user)
    replacements['<mm-undigest-radio-button>'] = mlist.FormatOptionButton(
        mm_cfg.Digests, 0, user)
    replacements['<mm-plain-digests-button>'] = mlist.FormatOptionButton(
        mm_cfg.DisableMime, 1, user)
    replacements['<mm-mime-digests-button>'] = mlist.FormatOptionButton(
        mm_cfg.DisableMime, 0, user)
    replacements['<mm-global-mime-button>'] = (CheckBox('mime-globally',
                                                        1,
                                                        checked=0).Format())
    replacements['<mm-delivery-enable-button>'] = mlist.FormatOptionButton(
        mm_cfg.DisableDelivery, 0, user)
    replacements['<mm-delivery-disable-button>'] = mlist.FormatOptionButton(
        mm_cfg.DisableDelivery, 1, user)
    replacements['<mm-disabled-notice>'] = mlist.FormatDisabledNotice(user)
    replacements['<mm-dont-ack-posts-button>'] = mlist.FormatOptionButton(
        mm_cfg.AcknowledgePosts, 0, user)
    replacements['<mm-ack-posts-button>'] = mlist.FormatOptionButton(
        mm_cfg.AcknowledgePosts, 1, user)
    replacements['<mm-receive-own-mail-button>'] = mlist.FormatOptionButton(
        mm_cfg.DontReceiveOwnPosts, 0, user)
    replacements['<mm-dont-receive-own-mail-button>'] = (
        mlist.FormatOptionButton(mm_cfg.DontReceiveOwnPosts, 1, user))
    replacements['<mm-dont-get-password-reminder-button>'] = (
        mlist.FormatOptionButton(mm_cfg.SuppressPasswordReminder, 1, user))
    replacements['<mm-get-password-reminder-button>'] = (
        mlist.FormatOptionButton(mm_cfg.SuppressPasswordReminder, 0, user))
    replacements['<mm-public-subscription-button>'] = (
        mlist.FormatOptionButton(mm_cfg.ConcealSubscription, 0, user))
    replacements['<mm-hide-subscription-button>'] = mlist.FormatOptionButton(
        mm_cfg.ConcealSubscription, 1, user)
    replacements['<mm-dont-receive-duplicates-button>'] = (
        mlist.FormatOptionButton(mm_cfg.DontReceiveDuplicates, 1, user))
    replacements['<mm-receive-duplicates-button>'] = (mlist.FormatOptionButton(
        mm_cfg.DontReceiveDuplicates, 0, user))
    replacements['<mm-unsubscribe-button>'] = (
        mlist.FormatButton('unsub', _('Unsubscribe')) + '<br>' +
        CheckBox('unsubconfirm', 1, checked=0).Format() +
        _('<em>Yes, I really want to unsubscribe</em>'))
    replacements['<mm-new-pass-box>'] = mlist.FormatSecureBox('newpw')
    replacements['<mm-confirm-pass-box>'] = mlist.FormatSecureBox('confpw')
    replacements['<mm-change-pass-button>'] = (mlist.FormatButton(
        'changepw', _("Change My Password")))
    replacements['<mm-other-subscriptions-submit>'] = (mlist.FormatButton(
        'othersubs', _('List my other subscriptions')))
    replacements['<mm-form-start>'] = (mlist.FormatFormStart(
        'options', user, mlist=mlist, contexts=AUTH_CONTEXTS, user=user))
    replacements['<mm-user>'] = user
    replacements['<mm-presentable-user>'] = presentable_user
    replacements['<mm-email-my-pw>'] = mlist.FormatButton(
        'emailpw', (_('Email My Password To Me')))
    replacements['<mm-umbrella-notice>'] = (mlist.FormatUmbrellaNotice(
        user, _("password")))
    replacements['<mm-logout-button>'] = (mlist.FormatButton(
        'logout', _('Log out')))
    replacements['<mm-options-submit-button>'] = mlist.FormatButton(
        'options-submit', _('Submit My Changes'))
    replacements['<mm-global-pw-changes-button>'] = (CheckBox(
        'pw-globally', 1, checked=0).Format())
    replacements['<mm-global-deliver-button>'] = (CheckBox('deliver-globally',
                                                           1,
                                                           checked=0).Format())
    replacements['<mm-global-remind-button>'] = (CheckBox('remind-globally',
                                                          1,
                                                          checked=0).Format())
    replacements['<mm-global-nodupes-button>'] = (CheckBox('nodupes-globally',
                                                           1,
                                                           checked=0).Format())

    days = int(mm_cfg.PENDING_REQUEST_LIFE / mm_cfg.days(1))
    if days > 1:
        units = _('days')
    else:
        units = _('day')
    replacements['<mm-pending-days>'] = _('%(days)d %(units)s')

    replacements['<mm-new-address-box>'] = mlist.FormatBox('new-address')
    replacements['<mm-confirm-address-box>'] = mlist.FormatBox(
        'confirm-address')
    replacements['<mm-change-address-button>'] = mlist.FormatButton(
        'change-of-address', _('Change My Address and Name'))
    replacements['<mm-global-change-of-address>'] = CheckBox(
        'changeaddr-globally', 1, checked=0).Format()
    replacements['<mm-fullname-box>'] = mlist.FormatBox('fullname',
                                                        value=fullname)

    # Create the topics radios.  BAW: what if the list admin deletes a topic,
    # but the user still wants to get that topic message?
    usertopics = mlist.getMemberTopics(user)
    if mlist.topics:
        table = Table(border="0")
        for name, pattern, description, emptyflag in mlist.topics:
            if emptyflag:
                continue
            quotedname = urllib.parse.quote_plus(name)
            details = Link(
                mlist.GetScriptURL('options') + '/%s/?VARHELP=%s' %
                (user, quotedname), ' (Details)')
            if name in usertopics:
                checked = 1
            else:
                checked = 0
            table.AddRow([
                CheckBox('usertopic', quotedname, checked=checked),
                name + details.Format()
            ])
        topicsfield = table.Format()
    else:
        topicsfield = _('<em>No topics defined</em>')
    replacements['<mm-topics>'] = topicsfield
    replacements['<mm-suppress-nonmatching-topics>'] = (
        mlist.FormatOptionButton(mm_cfg.ReceiveNonmatchingTopics, 0, user))
    replacements['<mm-receive-nonmatching-topics>'] = (
        mlist.FormatOptionButton(mm_cfg.ReceiveNonmatchingTopics, 1, user))

    if cpuser is not None:
        replacements['<mm-case-preserved-user>'] = _('''
You are subscribed to this list with the case-preserved address
<em>%(cpuser)s</em>.''')
    else:
        replacements['<mm-case-preserved-user>'] = ''

    page_text = mlist.ParseTags('options.html', replacements, userlang)
    if not (mlist.digestable or mlist.getMemberOption(user, mm_cfg.Digests)):
        page_text = DIGRE.sub('', page_text)
    doc.AddItem(page_text)
Пример #52
0
def loginpage(mlist, doc, user, lang):
    realname = mlist.real_name
    actionurl = mlist.GetScriptURL('options')
    if user is None:
        title = _('%(realname)s list: member options login page')
        extra = _('email address and ')
    else:
        safeuser = Utils.websafe(user)
        title = _('%(realname)s list: member options for user %(safeuser)s')
        obuser = Utils.ObscureEmail(user)
        extra = ''
    # Set up the title
    doc.SetTitle(title)
    # We use a subtable here so we can put a language selection box in
    table = Table(width='100%', border=0, cellspacing=4, cellpadding=5)
    # If only one language is enabled for this mailing list, omit the choice
    # buttons.
    table.AddRow([Center(Header(2, title))])
    table.AddCellInfo(table.GetCurrentRowIndex(),
                      0,
                      bgcolor=mm_cfg.WEB_HEADER_COLOR)
    if len(mlist.GetAvailableLanguages()) > 1:
        langform = Form(actionurl)
        langform.AddItem(
            SubmitButton('displang-button', _('View this page in')))
        langform.AddItem(mlist.GetLangSelectBox(lang))
        if user:
            langform.AddItem(Hidden('email', user))
        table.AddRow([Center(langform)])
    doc.AddItem(table)
    # Preamble
    # Set up the login page
    form = Form(actionurl)
    form.AddItem(Hidden('language', lang))
    table = Table(width='100%', border=0, cellspacing=4, cellpadding=5)
    table.AddRow([
        _("""In order to change your membership option, you must
    first log in by giving your %(extra)smembership password in the section
    below.  If you don't remember your membership password, you can have it
    emailed to you by clicking on the button below.  If you just want to
    unsubscribe from this list, click on the <em>Unsubscribe</em> button and a
    confirmation message will be sent to you.

    <p><strong><em>Important:</em></strong> From this point on, you must have
    cookies enabled in your browser, otherwise none of your changes will take
    effect.
    """)
    ])
    # Password and login button
    ptable = Table(width='50%', border=0, cellspacing=4, cellpadding=5)
    if user is None:
        ptable.AddRow([Label(_('Email address:')), TextBox('email', size=20)])
    else:
        ptable.AddRow([Hidden('email', user)])
    ptable.AddRow([Label(_('Password:'******'password', size=20)])
    ptable.AddRow([Center(SubmitButton('login', _('Log in')))])
    ptable.AddCellInfo(ptable.GetCurrentRowIndex(), 0, colspan=2)
    table.AddRow([Center(ptable)])
    # Unsubscribe section
    table.AddRow([Center(Header(2, _('Unsubscribe')))])
    table.AddCellInfo(table.GetCurrentRowIndex(),
                      0,
                      bgcolor=mm_cfg.WEB_HEADER_COLOR)

    table.AddRow([
        _("""By clicking on the <em>Unsubscribe</em> button, a
    confirmation message will be emailed to you.  This message will have a
    link that you should click on to complete the removal process (you can
    also confirm by email; see the instructions in the confirmation
    message).""")
    ])

    table.AddRow([Center(SubmitButton('login-unsub', _('Unsubscribe')))])
    # Password reminder section
    table.AddRow([Center(Header(2, _('Password reminder')))])
    table.AddCellInfo(table.GetCurrentRowIndex(),
                      0,
                      bgcolor=mm_cfg.WEB_HEADER_COLOR)

    table.AddRow([
        _("""By clicking on the <em>Remind</em> button, your
    password will be emailed to you.""")
    ])

    table.AddRow([Center(SubmitButton('login-remind', _('Remind')))])
    # Finish up glomming together the login page
    form.AddItem(table)
    doc.AddItem(form)
    doc.AddItem(mlist.GetMailmanFooter())
Пример #53
0
def save_attachment(mlist, msg, dir, filter_html=True, patches=None, sigs=None):
    fsdir = os.path.join(mlist.archive_dir(), dir)
    makedirs(fsdir)
    # Figure out the attachment type and get the decoded data
    decodedpayload = msg.get_payload(decode=True)
    # BAW: mimetypes ought to handle non-standard, but commonly found types,
    # e.g. image/jpg (should be image/jpeg).  For now we just store such
    # things as application/octet-streams since that seems the safest.
    ctype = msg.get_content_type()
    # i18n file name is encoded
    lcset = Utils.GetCharSet(mlist.preferred_language)
    filename = Utils.oneline(msg.get_filename(""), lcset)
    # filename, fnext = os.path.splitext(filename) #won't work with double extensions like .patch.sig
    fnext = None
    try:
        (filename, fnext) = filename.split(".", 1)
        if filename == "":
            filename = None
        if fnext != "":
            fnext = "." + fnext
    except:
        pass
    # For safety, we should confirm this is valid ext for content-type
    # but we can use fnext if we introduce fnext filtering
    if mm_cfg.SCRUBBER_USE_ATTACHMENT_FILENAME_EXTENSION:
        # HTML message doesn't have filename :-(
        # if it's text/plain, use '.txt', otherwise we'd end with '.ksh' or so
        ext = fnext or guess_extension(ctype, ".txt")
    else:
        ext = guess_extension(ctype, fnext)
    if not ext:
        # We don't know what it is, so assume it's just a shapeless
        # application/octet-stream, unless the Content-Type: is
        # message/rfc822, in which case we know we'll coerce the type to
        # text/plain below.
        if ctype == "message/rfc822":
            ext = ".txt"
        else:
            ext = ".bin"
    # Allow only alphanumerics, dash, underscore, and dot
    ext = sre.sub("", ext)
    path = None
    extra = ""
    sha = ""
    msgfrom = ""
    do_write_file = True
    if mm_cfg.SCRUBBER_ADD_PAYLOAD_HASH_FILENAME or not filename:
        sha = msg.get(mm_cfg.SCRUBBER_SHA1SUM_HEADER)
        if sha:
            # no need to clutter headers
            del msg[mm_cfg.SCRUBBER_SHA1SUM_HEADER]
            msgfrom = msg[mm_cfg.SCRUBBER_SIGNEDBY_HEADER]
        else:
            sha = sha_new(decodedpayload).hexdigest()
    # We need a lock to calculate the next attachment number
    lockfile = os.path.join(fsdir, "attachments.lock")
    lock = LockFile.LockFile(lockfile)
    lock.lock()
    try:
        # Now base the filename on what's in the attachment, uniquifying it if
        # necessary.
        if not filename or mm_cfg.SCRUBBER_DONT_USE_ATTACHMENT_FILENAME:
            filebase = "attachment"
        else:
            # Sanitize the filename given in the message headers
            parts = pre.split(filename)
            filename = parts[-1]
            # Strip off leading dots
            filename = dre.sub("", filename)
            # Allow only alphanumerics, dash, underscore, and dot
            filename = sre.sub("", filename)
            # If the filename's extension doesn't match the type we guessed,
            # which one should we go with?  For now, let's go with the one we
            # guessed so attachments can't lie about their type.  Also, if the
            # filename /has/ no extension, then tack on the one we guessed.
            # The extension was removed from the name above.
            filebase = filename

        # Now we're looking for a unique name for this file on the file
        # system.  If msgdir/filebase.ext isn't unique, we'll add a counter
        # after filebase, e.g. msgdir/filebase-cnt.ext
        counter = 0
        while True:
            path = os.path.join(fsdir, filebase + extra + ext)
            # Generally it is not a good idea to test for file existance
            # before just trying to create it, but the alternatives aren't
            # wonderful (i.e. os.open(..., O_CREAT | O_EXCL) isn't
            # NFS-safe).  Besides, we have an exclusive lock now, so we're
            # guaranteed that no other process will be racing with us.
            if os.path.exists(path):
                counter += 1
                extra = "-%04d" % counter
            else:
                break
        filename = filebase + extra + ext

        if mm_cfg.SCRUBBER_ADD_PAYLOAD_HASH_FILENAME:
            # Make content hash to attachment
            linkdir = os.path.join(fsdir, "..", "links")
            makedirs(linkdir)
            if msgfrom:
                dst = os.path.join(linkdir, msgfrom + "_" + sha)
            else:
                dst = os.path.join(linkdir, sha)
            src = os.path.join(fsdir, filename)
            try:
                os.symlink(src, dst)
            except:
                syslog("gpg", "Duplicate attachment: %s/%s msgfrom: %s sha1: %s" % (fsdir, filename, msgfrom, sha))
                # To deduplicate would need to parse, etc.
                # filename = os.readlink(dst)
                # do_write_file = False

    finally:
        lock.unlock()
    if do_write_file:
        # `path' now contains the unique filename for the attachment.  There's
        # just one more step we need to do.  If the part is text/html and
        # ARCHIVE_HTML_SANITIZER is a string (which it must be or we wouldn't be
        # here), then send the attachment through the filter program for
        # sanitization
        if filter_html and ctype == "text/html":
            base, ext = os.path.splitext(path)
            tmppath = base + "-tmp" + ext
            fp = open(tmppath, "w")
            try:
                fp.write(decodedpayload)
                fp.close()
                cmd = mm_cfg.ARCHIVE_HTML_SANITIZER % {"filename": tmppath}
                progfp = os.popen(cmd, "r")
                decodedpayload = progfp.read()
                status = progfp.close()
                if status:
                    syslog("error", "HTML sanitizer exited with non-zero status: %s", status)
            finally:
                os.unlink(tmppath)
            # BAW: Since we've now sanitized the document, it should be plain
            # text.  Blarg, we really want the sanitizer to tell us what the type
            # if the return data is. :(
            ext = ".txt"
            path = base + ".txt"
        # Is it a message/rfc822 attachment?
        elif ctype == "message/rfc822":
            submsg = msg.get_payload()
            # BAW: I'm sure we can eventually do better than this. :(
            decodedpayload = Utils.websafe(str(submsg))
        fp = open(path, "w")
        fp.write(decodedpayload)
        fp.close()
    # Now calculate the url
    baseurl = mlist.GetBaseArchiveURL()
    # Private archives will likely have a trailing slash.  Normalize.
    if baseurl[-1] <> "/":
        baseurl += "/"
    url = baseurl + "%s/%s%s%s" % (dir, filebase, extra, ext)
    if sha:
        url += "?sha1=" + sha

    if sigs is not None and (ext.endswith(".sig") or ctype == "application/pgp-signature"):
        sigs.append({"id": sha, "name": filebase, "file": os.path.join(dir, filename), "url": url})
    elif patches is not None and ctype != "text/html" and ctype != "message/rfc822":
        patches.append({"id": sha, "name": filebase, "file": os.path.join(dir, filename), "url": url})

    # A trailing space in url string may save users who are using
    # RFC-1738 compliant MUA (Not Mozilla).
    # Trailing space will definitely be a problem with format=flowed.
    # Bracket the URL instead.
    # '<' + url + '>' Done by caller instead.
    return url
Пример #54
0
 def __init__(self, name, value='', size=mm_cfg.TEXTFIELDWIDTH):
     if isinstance(value, str):
         safevalue = Utils.websafe(value)
     else:
         safevalue = value
     InputObj.__init__(self, name, "TEXT", safevalue, checked=0, size=size)