示例#1
0
def main():
    doc = Document()
    doc.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE)

    cgidata = cgi.FieldStorage()
    parts = Utils.GetPathPieces()
    if parts:
        # Bad URL specification
        title = _('Bad URL specification')
        doc.SetTitle(title)
        doc.AddItem(
            Header(3, Bold(FontAttr(title, color='#ff0000', size='+2'))))
        syslog('error', 'Bad URL specification: %s', parts)
    elif cgidata.has_key('doit'):
        # We must be processing the list creation request
        process_request(doc, cgidata)
    elif cgidata.has_key('clear'):
        request_creation(doc)
    else:
        # Put up the list creation request form
        request_creation(doc)
    doc.AddItem('<hr>')
    # Always add the footer and print the document
    doc.AddItem(_('Return to the ') +
                Link(Utils.ScriptURL('listinfo'),
                     _('general list overview')).Format())
    doc.AddItem(_('<br>Return to the ') +
                Link(Utils.ScriptURL('admin'),
                     _('administrative list overview')).Format())
    doc.AddItem(MailmanLogo())
    print doc.Format()
示例#2
0
    def add_members (self, save=False):
        """Add any email addressses that are provided in the create form."""

        if self.welcome == '':
            text = ('Welcome to %s. Visit the List Server to ' +
            'manage your subscriptions') % self.ln
        else:
            text = self.welcome

        for key in self.cgidata.keys():
            if re.match('^lc_member_', key):
                fn, em = parseaddr(self.cgival(key).lower().strip())
                userdesc = UserDesc(em, fn, mm_cfg.SSO_STOCK_USER_PWD, False)
                try:
                    self.ml.ApprovedAddMember(userdesc, True, text,
                                              whence='SSO List Creation Time')
                    syslog('sso',
                           'Successfully added %s to list: %s' % (em,
                                                                  self.ln))
                except Errors.MMAlreadyAMember:
                    ## FIXME: Need to find some way of communicating this
                    ## case to the user. As thisi s a new list, this can only
                    ## happen if the same address is given by the admin... hm
                    syslog('sso',
                           '%s already a member of listL %s' % (em, self.ln))

        if save:
            self.ml.Save()
示例#3
0
def matches_p(sender, nonmembers, listname):
    # First strip out all the regular expressions and listnames
    plainaddrs = [addr for addr in nonmembers if not (addr.startswith('^')
                                                 or addr.startswith('@'))]
    addrdict = Utils.List2Dict(plainaddrs, foldcase=1)
    if addrdict.has_key(sender):
        return 1
    # Now do the regular expression matches
    for are in nonmembers:
        if are.startswith('^'):
            try:
                cre = re.compile(are, re.IGNORECASE)
            except re.error:
                continue
            if cre.search(sender):
                return 1
        elif are.startswith('@'):
            # XXX Needs to be reviewed for list@domain names.
            try:
                mname = are[1:].lower().strip()
                if mname == listname:
                    # don't reference your own list
                    syslog('error',
                        '*_these_nonmembers in %s references own list',
                        listname)
                else:
                    mother = MailList(mname, lock=0)
                    if mother.isMember(sender):
                        return 1
            except Errors.MMUnknownListError:
                syslog('error',
                  '*_these_nonmembers in %s references non-existent list %s',
                  listname, mname)
    return 0
示例#4
0
def report_submission(msgid, message, inprogress=False):
    """
    :return: URL of the HTML document
    """
    if not mm_cfg.POST_TRACKING_URLBASE or not mm_cfg.POST_TRACKING_PATH:
        return ""
    sha1hex = sha_new(msgid).hexdigest()
    fname = "%s.html" % sha1hex
    tmpname = ".%s.tmp" % sha1hex
    fullfname = os.path.join(mm_cfg.POST_TRACKING_PATH, fname)
    tmpname = os.path.join(mm_cfg.POST_TRACKING_PATH, tmpname)
    doc = """<html><head><title>Mailman tracker</title>%s</head><body>
    <h3>Message ID %s</h3>
    <p>
    %s
    </p>
    </body></html>
    """
    meta = '<meta http-equiv="refresh" content="30"/>' if inprogress else ""

    try:
        with open(tmpname, "w") as reportfile:
            reportfile.write(doc % (meta, websafe(msgid), message))
        os.rename(tmpname, fullfname)
    except OSError, e:
        syslog("error", "report_submission failed: %s", e)
        return ""
示例#5
0
def bulkdeliver(mlist, msg, msgdata, envsender, failures, conn):
    # Do some final cleanup of the message header.  Start by blowing away
    # any the Sender: and Errors-To: headers so remote MTAs won't be
    # tempted to delivery bounces there instead of our envelope sender
    #
    # BAW An interpretation of RFCs 2822 and 2076 could argue for not touching
    # the Sender header at all.  Brad Knowles points out that MTAs tend to
    # wipe existing Return-Path headers, and old MTAs may still honor
    # Errors-To while new ones will at worst ignore the header.
    del msg['sender']
    del msg['errors-to']
    msg['Sender'] = envsender
    msg['Errors-To'] = envsender
    # Get the plain, flattened text of the message, sans unixfrom
    msgtext = msg.as_string()
    refused = {}
    recips = msgdata['recips']
    msgid = msg['message-id']
    try:
        # Send the message
        refused = conn.sendmail(envsender, recips, msgtext)
    except smtplib.SMTPRecipientsRefused, e:
        syslog('smtp-failure', 'All recipients refused: %s, msgid: %s',
               e, msgid)
        refused = e.recipients
示例#6
0
def maybe_forward(mlist, msg):
    # Does the list owner want to get non-matching bounce messages?
    # If not, simply discard it.
    if mlist.bounce_unrecognized_goes_to_list_owner:
        adminurl = mlist.GetScriptURL('admin', absolute=1) + '/bounce'
        mlist.ForwardMessage(msg,
                             text=_("""\
The attached message was received as a bounce, but either the bounce format
was not recognized, or no member addresses could be extracted from it.  This
mailing list has been configured to send all unrecognized bounce messages to
the list administrator(s).

For more information see:
%(adminurl)s

"""),
                             subject=_('Uncaught bounce notification'),
                             tomoderators=0)
        syslog('bounce',
               '%s: forwarding unrecognized, message-id: %s',
               mlist.internal_name(),
               msg.get('message-id', 'n/a'))
    else:
        syslog('bounce',
               '%s: discarding unrecognized, message-id: %s',
               mlist.internal_name(),
               msg.get('message-id', 'n/a'))
示例#7
0
def do_discard(mlist, msg):
    sender = msg.get_sender()
    # Do we forward auto-discards to the list owners?
    if mlist.forward_auto_discards:
        lang = mlist.preferred_language
        varhelp = '%s/?VARHELP=privacy/sender/discard_these_nonmembers' % \
                  mlist.GetScriptURL('admin', absolute=1)
        nmsg = Message.UserNotification(mlist.GetOwnerEmail(),
                                        mlist.GetBouncesEmail(),
                                        _('Auto-discard notification'),
                                        lang=lang)
        nmsg.set_type('multipart/mixed')
        text = MIMEText(Utils.wrap(_(
            'The attached message has been automatically discarded.')),
                        _charset=Utils.GetCharSet(lang))
        nmsg.attach(text)

        decrypted = msg.get('X-Mailman-SLS-decrypted', '').lower()
        if decrypted == 'yes':
            syslog('gpg',
 'forwarding only headers of message from %s to listmaster to notify discard since message was decrypted',
 sender)
            msgtext = msg.as_string()
            (header, body) = msgtext.split("\n\n", 1)
            nmsg.attach(MIMEText(header))
        else:
            nmsg.attach(MIMEMessage(msg))

        nmsg.send(mlist)
    # Discard this sucker
    raise Errors.DiscardMessage
示例#8
0
 def importAllSubscriberKeys(self):
     gpg = self.getGPGObject()
     p = gpg.run(['--import'],create_fhs=['stdin','stdout','stderr'])
     t_out = AsyncRead(p.handles['stdout'])
     t_out.start()
     t_err = AsyncRead(p.handles['stderr'])
     t_err.start()
     for user in self.mlist.getMembers():
         key = self.mlist.getGPGKey(user)
         if key:
             p.handles['stdin'].write(key)
     p.handles['stdin'].close()
     t_out.join()
     t_err.join()
     # Ignore date from t_out
     result = t_err.data
     try:
         p.wait()
     except IOError:
         syslog('gpg','Error importing keys: %s' % result)
         return None
     self.checkPerms()
     key_ids= []
     for line in result.lower().splitlines():
         g = re.search('key ([0-9a-f]+):',line)
         if g!=None:
             key_ids.append('0x%s' % g.groups()[0])
     return key_ids
示例#9
0
def process(mlist, msg, msgdata):
    # Short circuit non-digestable lists.
    if not mlist.digestable or msgdata.get('isdigest'):
        return
    mboxfile = os.path.join(mlist.fullpath(), 'digest.mbox')
    omask = os.umask(007)
    try:
        mboxfp = open(mboxfile, 'a+')
    finally:
        os.umask(omask)
    mbox = Mailbox(mboxfp)
    mbox.AppendMessage(msg)
    # Calculate the current size of the accumulation file.  This will not tell
    # us exactly how big the MIME, rfc1153, or any other generated digest
    # message will be, but it's the most easily available metric to decide
    # whether the size threshold has been reached.
    mboxfp.flush()
    size = os.path.getsize(mboxfile)
    if size / 1024.0 >= mlist.digest_size_threshhold:
        # This is a bit of a kludge to get the mbox file moved to the digest
        # queue directory.
        try:
            # Enclose in try/except here because a error in send_digest() can
            # silently stop regular delivery.  Unsuccessful digest delivery
            # should be tried again by cron and the site administrator will be
            # notified of any error explicitly by the cron error message.
            mboxfp.seek(0)
            send_digests(mlist, mboxfp)
            os.unlink(mboxfile)
        except Exception, errmsg:
            # Bare except is generally prohibited in Mailman, but we can't
            # forecast what exceptions can occur here.
            syslog('error', 'send_digests() failed: %s', errmsg)
示例#10
0
    def request_edit (self):
        self._ml = self.all_mls[self.ln]
        err = self.errcheck(action='edit')
        if err:
            return err

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

        try:
            signal.signal(signal.SIGTERM, self.sigterm_handler)

            self.ml.Lock()
            self.set_ml_params()
            self.edit_members()
            self.set_ml_owners()
            self.ml.Save()
            syslog('sso', 'Successfully modified list config: %s' % self.ln)
        finally:
            # Now be sure to unlock the list.  It's okay if we get a signal
            # here because essentially, the signal handler will do the same
            # thing.  And unlocking is unconditional, so it's not an error if
            # we unlock while we're already unlocked.
            self.ml.Unlock()

        return None
示例#11
0
def decorate(mlist, template, what, extradict=None):
    # `what' is just a descriptive phrase used in the log message
    #
    # BAW: We've found too many situations where Python can be fooled into
    # interpolating too much revealing data into a format string.  For
    # example, a footer of "% silly %(real_name)s" would give a header
    # containing all list attributes.  While we've previously removed such
    # really bad ones like `password' and `passwords', it's much better to
    # provide a whitelist of known good attributes, then to try to remove a
    # blacklist of known bad ones.
    d = SafeDict({'real_name'     : mlist.real_name,
                  'list_name'     : mlist.internal_name(),
                  # For backwards compatibility
                  '_internal_name': mlist.internal_name(),
                  'host_name'     : mlist.host_name,
                  'web_page_url'  : mlist.web_page_url,
                  'description'   : mlist.description,
                  'info'          : mlist.info,
                  'cgiext'        : mm_cfg.CGIEXT,
                  })
    if extradict is not None:
        d.update(extradict)
    # Using $-strings?
    if getattr(mlist, 'use_dollar_strings', 0):
        template = Utils.to_percent(template)
    # Interpolate into the template
    try:
        text = re.sub(r'(?m)(?<!^--) +(?=\n)', '',
                      re.sub(r'\r\n', r'\n', template % d))
    except (ValueError, TypeError), e:
        syslog('error', 'Exception while calculating %s:\n%s', what, e)
        text = template
    def __init__(self, mlist, param):
        self._param = param
        self.__mlist = mlist
        self._members = None
        self._member_passwd = {}
        self._member_names = {}
        self._updatetime = 0
        # define the table and standard condition reflecting listname
        self._table = param["mailman_table"]
        self._where = "listname = '%s'" % (self.__mlist.internal_name())

        # define query for session management
        self._cookiename = param["cookiename"]
        self._queryCookieMail = param["queryCookieMail"]
        self._queryCookieId = param["queryCookieId"]
        self._queryIsAdmin = param["queryIsAdmin"]
        self._queryIsSiteAdmin = param["queryIsSiteAdmin"]
        self._queryIsMonitoring = param["queryIsMonitoring"]

        self.__db_connect__()
        if mm_cfg.MYSQL_MEMBER_DB_VERBOSE:
            # Message to indicate successful init.
            message = "DBMemberships " + "$Revision: 1.69 $ initialized with host: %s (%s)" % (
                mm_cfg.connection.get_host_info(),
                mm_cfg.connection.get_server_info(),
            )
            syslog("error", message)
            syslog("mysql", message)

        # add a cache memory
        self._cache = {}
        self._cachedate = 0
示例#13
0
 def _heartbeat(self):
     """Add a heartbeat to the log for a monitor to watch."""
     now = datetime.now()
     last_heartbeat = self.last_heartbeat
     if last_heartbeat is None or now - last_heartbeat >= self.heartbeat_frequency:
         syslog("xmlrpc", "--MARK--")
         self.last_heartbeat = now
示例#14
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
示例#15
0
文件: Utils.py 项目: bdraco/mailman
def Secure_MakeRandomPassword(length):
    bytesread = 0
    bytes = []
    fd = None
    try:
        while bytesread < length:
            try:
                # Python 2.4 has this on available systems.
                newbytes = os.urandom(length - bytesread)
            except (AttributeError, NotImplementedError):
                if fd is None:
                    try:
                        fd = os.open('/dev/urandom', os.O_RDONLY)
                    except OSError, e:
                        if e.errno <> errno.ENOENT:
                            raise
                        # We have no available source of cryptographically
                        # secure random characters.  Log an error and fallback
                        # to the user friendly passwords.
                        syslog('error',
                               'urandom not available, passwords not secure')
                        return UserFriendly_MakeRandomPassword(length)
                newbytes = os.read(fd, length - bytesread)
            bytes.append(newbytes)
            bytesread += len(newbytes)
        s = base64.encodestring(EMPTYSTRING.join(bytes))
        # base64 will expand the string by 4/3rds
        return s.replace('\n', '')[:length]
示例#16
0
 def HoldUnsubscription(self, addr):
     # Assure the database is open for writing
     self.__opendb()
     # Get the next unique id
     id = self.__nextid()
     # All we need to do is save the unsubscribing address
     self.__db[id] = (UNSUBSCRIPTION, addr)
     syslog('vette', '%s: held unsubscription request from %s',
            self.internal_name(), addr)
     # Possibly notify the administrator of the hold
     if self.admin_immed_notify:
         realname = self.real_name
         subject = _(
             'New unsubscription request from %(realname)s by %(addr)s')
         text = Utils.maketext(
             'unsubauth.txt',
             {'username'   : addr,
              ## cpanel patch
              'listname'   : self.real_name,
              'hostname'   : self.host_name,
              'admindb_url': self.GetScriptURL('admindb', absolute=1),
              }, mlist=self)
         # This message should appear to come from the <list>-owner so as
         # to avoid any useless bounce processing.
         owneraddr = self.GetOwnerEmail()
         msg = Message.UserNotification(owneraddr, owneraddr, subject, text,
                                        self.preferred_language)
         msg.send(self, **{'tomoderators': 1})
示例#17
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
示例#18
0
 def encryptSignMessage(self,msg,recipients):
     gpg = self.getGPGObject()
     params = ['--encrypt','--sign','--always-trust','--batch','--no-permission-warning']
     for i in recipients:
         params.append('-r')
         params.append(i)
     p = gpg.run(params, create_fhs=['stdin','stdout','stderr','passphrase'])
     t_out = AsyncRead(p.handles['stdout'])
     t_out.start()
     t_err = AsyncRead(p.handles['stderr'])
     t_err.start()
     p.handles['passphrase'].write(self.mlist.gpg_passphrase)
     p.handles['passphrase'].close()
     p.handles['stdin'].write(msg)
     p.handles['stdin'].close()
     t_out.join()
     t_err.join()
     ciphertext = t_out.data
     result = t_err.data
     try:
         p.wait()
     except IOError:
         syslog('gpg',"Error encrypting message: %s",result)
         return None
     return ciphertext
示例#19
0
def db_export(mlist):
    try:
        rootdir = mlist.archive_dir()
        conn = db_conn(mlist)
        return _db_export(conn,rootdir)
    except Exception,e:
        syslog('gpg','%s' % e)
示例#20
0
def verp_probe(mlist, msg):
    bmailbox, bdomain = Utils.ParseEmail(mlist.GetBouncesEmail())
    # Sadly not every MTA bounces VERP messages correctly, or consistently.
    # Fall back to Delivered-To: (Postfix), Envelope-To: (Exim) and
    # Apparently-To:, and then short-circuit if we still don't have anything
    # to work with.  Note that there can be multiple Delivered-To: headers so
    # we need to search them all (and we don't worry about false positives for
    # forwarded email, because only one should match VERP_REGEXP).
    vals = []
    for header in ('to', 'delivered-to', 'envelope-to', 'apparently-to'):
        vals.extend(msg.get_all(header, []))
    for field in vals:
        to = parseaddr(field)[1]
        if not to:
            continue                          # empty header
        mo = re.search(mm_cfg.VERP_PROBE_REGEXP, to)
        if not mo:
            continue                          # no match of regexp
        try:
            if bmailbox <> mo.group('bounces'):
                continue                      # not a bounce to our list
            # Extract the token and see if there's an entry
            token = mo.group('token')
            data = mlist.pend_confirm(token, expunge=False)
            if data is not None:
                return token
        except IndexError:
            syslog(
                'error',
                "VERP_PROBE_REGEXP doesn't yield the right match groups: %s",
                mm_cfg.VERP_PROBE_REGEXP)
    return None
示例#21
0
 def _dopipeline(self, mlist, msg, msgdata, pipeline):
     while pipeline:
         handler = pipeline.pop(0)
         modname = 'Mailman.Handlers.' + handler
         __import__(modname)
         try:
             pid = os.getpid()
             sys.modules[modname].process(mlist, msg, msgdata)
             # Failsafe -- a child may have leaked through.
             if pid <> os.getpid():
                 syslog('error', 'child process leaked thru: %s', modname)
                 os._exit(1)
         except Errors.DiscardMessage:
             # Throw the message away; we need do nothing else with it.
             syslog('vette', 'Message discarded, msgid: %s',
                    msg.get('message-id', 'n/a'))
             return 0
         except Errors.HoldMessage:
             # Let the approval process take it from here.  The message no
             # longer needs to be queued.
             return 0
         except Errors.RejectMessage, e:
             mlist.BounceMessage(msg, msgdata, e)
             return 0
         except:
示例#22
0
 def _dispose(self, mlist, msg, msgdata):
     # Make sure we have the most up-to-date state
     mlist.Load()
     if not msgdata.get('prepped'):
         prepare_message(mlist, msg, msgdata)
     try:
         # Flatten the message object, sticking it in a StringIO object
         fp = StringIO(msg.as_string())
         conn = None
         try:
             try:
                 nntp_host, nntp_port = Utils.nntpsplit(mlist.nntp_host)
                 conn = nntplib.NNTP(nntp_host, nntp_port,
                                     readermode=True,
                                     user=mm_cfg.NNTP_USERNAME,
                                     password=mm_cfg.NNTP_PASSWORD)
                 conn.post(fp)
             except nntplib.error_temp, e:
                 syslog('error',
                        '(NNTPDirect) NNTP error for list "%s": %s',
                        mlist.internal_name(), e)
             except socket.error, e:
                 syslog('error',
                        '(NNTPDirect) socket error for list "%s": %s',
                        mlist.internal_name(), e)
         finally:
             if conn:
                 conn.quit()
示例#23
0
   def __handlesubscription(self, record, value, comment):
       stime, addr, fullname, password, digest, lang = record
       if value == mm_cfg.DEFER:
           return DEFER
       elif value == mm_cfg.DISCARD:
           syslog('vette', '%s: discarded subscription request from %s',
                  self.internal_name(), addr)
       elif value == mm_cfg.REJECT:
           self.__refuse(_('Subscription request'), addr,
                         comment or _('[No reason given]'),
                         lang=lang)
           syslog('vette', """%s: rejected subscription request from %s
tReason: %s""", self.internal_name(), addr, comment or '[No reason given]')
       else:
           # subscribe
           assert value == mm_cfg.SUBSCRIBE
           try:
               userdesc = UserDesc(addr, fullname, password, digest, lang)
               self.ApprovedAddMember(userdesc, whence='via admin approval')
           except Errors.MMAlreadyAMember:
               # User has already been subscribed, after sending the request
               pass
           # TBD: disgusting hack: ApprovedAddMember() can end up closing
           # the request database.
           self.__opendb()
       return REMOVE
示例#24
0
def process(mlist, msg, msgdata):
    """Process the message object for the given list.

    The message object is an instance of Mailman.Message and must be fully
    prepared for delivery (i.e. all the appropriate headers must be set).  The
    message object can have the following attributes:

    recips - the list of recipients for the message (required)

    This function processes the message by handing off the delivery of the
    message to a sendmail (or sendmail clone) program.  It can raise a
    SendmailHandlerError if an error status was returned by the sendmail
    program.
    
    """
    recips = msgdata.get('recips')
    if not recips:
        # Nobody to deliver to!
        return
    # Use -f to set the envelope sender
    cmd = mm_cfg.SENDMAIL_CMD + ' -f ' + mlist.GetAdminEmail() + ' '
    # make sure the command line is of a manageable size
    recipchunks = []
    currentchunk = []
    chunklen = 0
    for r in recips:
        currentchunk.append(r)
        chunklen = chunklen + len(r) + 1
        if chunklen > MAX_CMDLINE:
            recipchunks.append(string.join(currentchunk))
            currentchunk = []
            chunklen = 0
    # pick up the last one
    if chunklen:
        recipchunks.append(string.join(currentchunk))
    # get all the lines of the message, since we're going to do this over and
    # over again
    msgtext = str(msg)
    # cycle through all chunks
    failedrecips = []
    for chunk in recipchunks:
        # TBD: SECURITY ALERT.  This invokes the shell!
        fp = os.popen(cmd + chunk, 'w')
        fp.write(msgtext)
        status = fp.close()
        if status:
            errcode = (status & 0xff00) >> 8
            syslog('post', 'post to %s from %s, size=%d, failure=%d' %
                   (mlist.internal_name(), msg.GetSender(),
                    len(msg.body), errcode))
            # TBD: can we do better than this?  What if only one recipient out
            # of the entire chunk failed?
            failedrecips.append(chunk)
        # Log the successful post
        syslog('post', 'post to %s from %s, size=%d, success' %
               (mlist.internal_name(), msg.GetSender(), len(msg.body)))
    if failedrecips:
        msgdata['recips'] = failedrecips
        raise HandlerAPI.SomeRecipientsFailed
示例#25
0
def AddOptionsTableItem(table, item, category, mlist, detailsp=1):
    """Add a row to an options table with the item description and value."""
    try:
	got = GetItemCharacteristics(item)
	varname, kind, params, dependancies, descr, elaboration = got
    except ValueError, msg:
        syslog('error', 'admin: %s' % msg)
        return Italic("<malformed option>")
示例#26
0
文件: Utils.py 项目: bdraco/mailman
def GetPathPieces(envar='PATH_INFO'):
    path = os.environ.get(envar)
    if path:
        if CRNLpat.search(path):
            path = CRNLpat.split(path)[0]
            syslog('error', 'Warning: Possible malformed path attack.')
        return [p for p in path.split('/') if p]
    return None
示例#27
0
def GetPathPieces(envar="PATH_INFO"):
    path = os.environ.get(envar)
    if path:
        if CRNLpat.search(path):
            path = CRNLpat.split(path)[0]
            syslog("error", "Warning: Possible malformed path attack.")
        return [p for p in path.split("/") if p]
    return None
示例#28
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
示例#29
0
    def getSMIMEMemberCertFile(self, member):
        recipfile = self._getSMIMEMemberCertFile(member)

        if not os.access(recipfile,os.F_OK):
            syslog('gpg', "No Member SMIME Certfile '%s' found", recipfile)
            return None

        syslog('gpg', "Using Member SMIME Certfile '%s'", recipfile)
        return recipfile
示例#30
0
def process_request(doc, cgidata, mlist):
    password = cgidata.getvalue('password', '').strip()
    try:
        delarchives = int(cgidata.getvalue('delarchives', '0'))
    except ValueError:
        delarchives = 0

    # Removing a list is limited to the list-creator (a.k.a. list-destroyer),
    # the list-admin, or the site-admin.  Don't use WebAuthenticate here
    # because we want to be sure the actual typed password is valid, not some
    # password sitting in a cookie.
    if mlist.Authenticate((mm_cfg.AuthCreator,
                           mm_cfg.AuthListAdmin,
                           mm_cfg.AuthSiteAdmin),
                          password) == mm_cfg.UnAuthorized:
        request_deletion(
            doc, mlist,
            _('You are not authorized to delete this mailing list'))
        return

    # Do the MTA-specific list deletion tasks
    if mm_cfg.MTA:
        modname = 'Mailman.MTA.' + mm_cfg.MTA
        __import__(modname)
        sys.modules[modname].remove(mlist, cgi=1)
    
    REMOVABLES = ['lists/%s']

    if delarchives:
        REMOVABLES.extend(['archives/private/%s',
                           'archives/private/%s.mbox',
                           'archives/public/%s',
                           'archives/public/%s.mbox',
                           ])

    problems = 0
    listname = mlist.internal_name()
    for dirtmpl in REMOVABLES:
        dir = os.path.join(mm_cfg.VAR_PREFIX, dirtmpl % listname)
        if os.path.islink(dir):
            try:
                os.unlink(dir)
            except OSError, e:
                if e.errno not in (errno.EACCES, errno.EPERM): raise
                problems += 1
                syslog('error',
                       'link %s not deleted due to permission problems',
                       dir)
        elif os.path.isdir(dir):
            try:
                shutil.rmtree(dir)
            except OSError, e:
                if e.errno not in (errno.EACCES, errno.EPERM): raise
                problems += 1
                syslog('error',
                       'directory %s not deleted due to permission problems',
                       dir)
示例#31
0
 def processUnixMailbox(self, input, start=None, end=None):
     mbox = ArchiverMailbox(input, self.maillist)
     if start is None:
         start = 0
     counter = 0
     if start:
         mbox.skipping(True)
     while counter < start:
         try:
             m = mbox.next()
         except Errors.DiscardMessage:
             continue
         if m is None:
             return
         counter += 1
     if start:
         mbox.skipping(False)
     while 1:
         try:
             pos = input.tell()
             m = mbox.next()
         except Errors.DiscardMessage:
             continue
         except Exception:
             syslog('error', 'uncaught archiver exception at filepos: %s',
                    pos)
             raise
         if m is None:
             break
         if m == '':
             # It was an unparseable message
             continue
         msgid = m.get('message-id', 'n/a')
         self.message(_('#%(counter)05d %(msgid)s'))
         a = self._makeArticle(m, self.sequence)
         self.sequence += 1
         self.add_article(a)
         if end is not None and counter >= end:
            break
         counter += 1
示例#32
0
def _update_maps():
    # Helper function to fix owner and mode.
    def fixom(file):
        # It's not necessary for the non-db file to be S_IROTH, but for
        # simplicity and compatibility with check_perms, we set it.
        stat = os.stat(file)
        if (stat[ST_MODE] & targetmode) != targetmode:
            os.chmod(file, stat[ST_MODE] | targetmode)
        dbfile = file + '.db'
        try:
            stat = os.stat(dbfile)
        except OSError as e:
            if e.errno != errno.ENOENT:
                raise
            return
        if (stat[ST_MODE] & targetmode) != targetmode:
            os.chmod(dbfile, stat[ST_MODE] | targetmode)
        user = mm_cfg.MAILMAN_USER
        if stat[ST_UID] != pwd.getpwnam(user)[2]:
            uid = pwd.getpwnam(user)[2]
            gid = grp.getgrnam(mm_cfg.MAILMAN_GROUP)[2]
            os.chown(dbfile, uid, gid)
    msg = 'command failed: %s (status: %s, %s)'
    acmd = mm_cfg.POSTFIX_ALIAS_CMD + ' ' + ALIASFILE
    status = (os.system(acmd) >> 8) & 0xff
    if status:
        errstr = os.strerror(status)
        syslog('error', msg, acmd, status, errstr)
        raise RuntimeError(msg % (acmd, status, errstr))
    # Fix owner and mode of .db if needed.
    fixom(ALIASFILE)
    if os.path.exists(VIRTFILE):
        vcmd = mm_cfg.POSTFIX_MAP_CMD + ' ' + VIRTFILE
        status = (os.system(vcmd) >> 8) & 0xff
        if status:
            errstr = os.strerror(status)
            syslog('error', msg, vcmd, status, errstr)
            raise RuntimeError(msg % (vcmd, status, errstr))
        # Fix owner and mode of .db if needed.
        fixom(VIRTFILE)
示例#33
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
示例#34
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
示例#35
0
    def _resynchronize(self, actions, statuses):
        """Process resynchronization actions.

        actions is a sequence of 2-tuples specifying what needs to be
        resynchronized.  The tuple is of the form (listname, current-status).

        statuses is a dictionary mapping team names to one of the strings
        'success' or 'failure'.
        """
        syslog('xmlrpc', 'resynchronizing: %s',
               COMMASPACE.join(sorted(name for (name, status) in actions)))
        for name, status in actions:
            # There's no way to really know whether the original action
            # succeeded or not, however, it's unlikely that an action would
            # fail leaving the mailing list in a usable state.  Therefore, if
            # the list is loadable and lockable, we'll say it succeeded.
            try:
                mlist = MailList(name)
            except Errors.MMUnknownListError:
                # The list doesn't exist on the Mailman side, so if its status
                # is CONSTRUCTING, we can create it now.
                if status == 'constructing':
                    if self._create(name):
                        statuses[name] = ('resynchronize', 'success')
                    else:
                        statuses[name] = ('resynchronize', 'failure')
                else:
                    # Any other condition leading to an unknown list is a
                    # failure state.
                    statuses[name] = ('resynchronize', 'failure')
            except:
                # Any other exception is also a failure.
                statuses[name] = ('resynchronize', 'failure')
                log_exception('Mailing list does not load: %s', name)
            else:
                # The list loaded just fine, so it successfully
                # resynchronized.  Be sure to unlock it!
                mlist.Unlock()
                statuses[name] = ('resynchronize', 'success')
示例#36
0
    def handler (self):
        listname = self.cgidata.getvalue('list')
        action   = self.cgidata.getvalue('action').lower().strip()

        syslog('sso', 'User: %s; Listname: %s; action: %s' % (self.curr_user,
                                                              listname, action))

        mlist = MailList.MailList(listname)
        userdesc = UserDesc(self.curr_user, u'', mm_cfg.SSO_STOCK_USER_PWD,
                            False)

        if action == 'join':
            try:
                text = ('Welcome to %s. Visit the List Server to ' +
                        'manage your subscriptions') % listname
                mlist.ApprovedAddMember(userdesc, True, text,
                                        whence='SSO Web Interface')
                mlist.Save()
                self.kwargs_add('notice_success', True)
                self.kwargs_add('notice_text',
                                'Successfully added to list: %s' % listname)
            except Errors.MMAlreadyAMember:
                self.kwargs_add('notice_success', False)
                self.kwargs_add('notice_text',
                                'You are already subscribed to %s' % listname)
        elif action == 'leave':
            try:
                mlist.ApprovedDeleteMember(self.curr_user)
                mlist.Save()
                self.kwargs_add('notice_success', True)
                self.kwargs_add('notice_text',
                                'Successfully removed from list: %s' % listname)
            except Errors.NotAMemberError:
                # User has already been unsubscribed
                self.kwargs_add('notice_success', False)
                self.kwargs_add('notice_text',
                                'You are not a member of %s' % listname)

        self.render()
示例#37
0
文件: Utils.py 项目: rfjakob/mailman2
def IsDMARCProhibited(mlist, email):
    if not dns_resolver:
        return False

    email = email.lower()
    at_sign = email.find('@')
    if at_sign < 1:
        return False
    dmarc_domain = '_dmarc.' + email[at_sign + 1:]

    try:
        resolver = dns.resolver.Resolver()
        resolver.timeout = float(mm_cfg.DMARC_RESOLVER_TIMEOUT)
        resolver.lifetime = float(mm_cfg.DMARC_RESOLVER_LIFETIME)
        txt_recs = resolver.query(dmarc_domain, dns.rdatatype.TXT)
    except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer):
        return False
    except DNSException, e:
        syslog('error',
               'DNSException: Unable to query DMARC policy for %s (%s). %s',
               email, dmarc_domain, e.__class__)
        return False
示例#38
0
    def _modify(self, actions, statuses):
        """Process mailing list modification actions.

        actions is a sequence of (team_name, modifications) tuples where the
        team_name is the name of the mailing list to create and modifications
        is a dictionary of values to set on the mailing list.

        statuses is a dictionary mapping team names to one of the strings
        'success' or 'failure'.
        """
        for team_name, modifications in actions:
            # First, validate the modification keywords.
            list_settings = {}
            for key in attrmap:
                if key in modifications:
                    list_settings[attrmap[key]] = modifications[key]
                    del modifications[key]
            if modifications:
                statuses[team_name] = ('modify', 'failure')
                syslog('xmlrpc', 'Unexpected modify settings: %s',
                       COMMASPACE.join(modifications))
                continue
            try:
                mlist = MailList(team_name)
                try:
                    for key, value in list_settings.items():
                        setattr(mlist, key, value)
                    mlist.Save()
                finally:
                    mlist.Unlock()
            # We have to use a bare except here because of the legacy string
            # exceptions that Mailman can raise.
            except:
                log_exception('List modification error for team: %s',
                              team_name)
                statuses[team_name] = ('modify', 'failure')
            else:
                statuses[team_name] = ('modify', 'success')
示例#39
0
 def _check_list_actions(self):
     """See if there are any list actions to perform."""
     try:
         actions = self._proxy.getPendingActions()
     except (xmlrpclib.ProtocolError, socket.error) as error:
         log_exception('Cannot talk to Launchpad:\n%s', error)
         return
     except xmlrpclib.Fault as error:
         log_exception('Launchpad exception: %s', error)
         return
     if actions:
         syslog('xmlrpc', 'Received these actions: %s',
                COMMASPACE.join(actions))
     else:
         return
     # There are three actions that can currently be taken.  A create
     # action creates a mailing list, possibly with some defaults, a modify
     # changes the settings on some existing mailing list, and a deactivate
     # means that the list should be deactivated.  This latter doesn't have
     # a directly corresponding semantic at the Mailman layer -- if a
     # mailing list exists, it's activated.  We'll take it to mean that the
     # list should be deleted, but its archives should remain.
     statuses = {}
     if 'create' in actions:
         self._create_or_reactivate(actions['create'], statuses)
         del actions['create']
     if 'modify' in actions:
         self._modify(actions['modify'], statuses)
         del actions['modify']
     if 'deactivate' in actions:
         self._deactivate(actions['deactivate'], statuses)
         del actions['deactivate']
     if 'unsynchronized' in actions:
         self._resynchronize(actions['unsynchronized'], statuses)
         del actions['unsynchronized']
     # Any other keys should be ignored because they specify actions that
     # we know nothing about.  We'll log them to Mailman's log files
     # though.
     if actions:
         syslog('xmlrpc', 'Invalid xmlrpc action keys: %s',
                COMMASPACE.join(actions))
     # Report the statuses to Launchpad.  Do this individually so as to
     # reduce the possibility that a bug in Launchpad causes the reporting
     # all subsequent mailing lists statuses to fail.  The reporting of
     # status triggers synchronous operations in Launchpad, such as
     # notifying team admins that their mailing list is ready, and those
     # operations could fail for spurious reasons.  That shouldn't affect
     # the status reporting for any other list.  This is a little more
     # costly, but it's not that bad.
     for team_name, (action, status) in statuses.items():
         this_status = {team_name: status}
         try:
             self._proxy.reportStatus(this_status)
             syslog('xmlrpc', '[%s] %s: %s' % (team_name, action, status))
         except (xmlrpclib.ProtocolError, socket.error) as error:
             log_exception('Cannot talk to Launchpad:\n%s', error)
         except xmlrpclib.Fault as error:
             log_exception('Launchpad exception: %s', error)
示例#40
0
def process(mlist, msg, msgdata):
    """Check the message size (approximately) against hard and soft limits.

    If the message is below the soft limit, do nothing.  This allows the
    message to be posted without moderation, assuming no other handler get
    triggered of course.

    Messages between the soft and hard limits get moderated in the Launchpad
    web u/i, just as non-member posts would.  Personal standing does not
    override the size checks.

    Messages above the hard limit get logged and discarded.  In production, we
    should never actually hit the hard limit.  The Exim in front of
    lists.launchpad.net has its own hard limit of 50MB (which is the
    production standard Mailman hard limit value), so messages larger than
    this should never even show up.
    """
    # Calculate the message size by turning it back into a string.  In Mailman
    # 3.0 this calculation is done on initial message parse so it will be
    # quicker and not consume so much memory.  But since the hard limit is
    # 50MB I don't think we can actually get into any real trouble here, as
    # long as we can trust Python's reference counter.
    message_size = len(msg.as_string())
    # Hard and soft limits are specified in bytes.
    if message_size < mm_cfg.LAUNCHPAD_SOFT_MAX_SIZE:
        # Nothing to do.
        return
    if message_size < mm_cfg.LAUNCHPAD_HARD_MAX_SIZE:
        # Hold the message in Mailman.  See lpmoderate.py for similar
        # algorithm.
        # There is a limit to the size that can be stored in Lp. Send
        # a trucated copy of the message that has enough information for
        # the moderator to make a decision.
        hold(mlist, truncated_message(msg), msgdata, 'Too big')
    # The message is larger than the hard limit, so log and discard.
    syslog('vette', 'Discarding message w/size > hard limit: %s',
           msg.get('message-id', 'n/a'))
    raise Errors.DiscardMessage
示例#41
0
def maybe_forward(mlist, msg):
    # Does the list owner want to get non-matching bounce messages?
    # If not, simply discard it.
    if mlist.bounce_unrecognized_goes_to_list_owner:
        adminurl = mlist.GetScriptURL('admin', absolute=1) + '/bounce'
        mlist.ForwardMessage(msg,
                             text=_("""\
The attached message was received as a bounce, but either the bounce format
was not recognized, or no member addresses could be extracted from it.  This
mailing list has been configured to send all unrecognized bounce messages to
the list administrator(s).

For more information see:
%(adminurl)s

"""),
                             subject=_('Uncaught bounce notification'),
                             tomoderators=0)
        syslog('bounce', '%s: forwarding unrecognized, message-id: %s',
               mlist.internal_name(), msg.get('message-id', 'n/a'))
    else:
        syslog('bounce', '%s: discarding unrecognized, message-id: %s',
               mlist.internal_name(), msg.get('message-id', 'n/a'))
示例#42
0
def process(mlist, msg, msgdata):
    if msgdata.get('approved'):
        return
    
    score, symbols = check_message(mlist, str(msg))

    if MEMBER_BONUS != 0:
        for sender in msg.get_senders():
            if mlist.isMember(sender) or \
                   mlist.GetPattern(sender, mlist.accept_these_nonmembers, at_list='accept_these_nonmembers'):
                score -= MEMBER_BONUS
                break

    if score > DISCARD_SCORE:
        listname = mlist.real_name
        sender = msg.get_sender()
        syslog('vette', '%s post from %s discarded: '
                        'SpamAssassin score was %g (discard threshold is %g)'
                          % (listname, sender, score, DISCARD_SCORE))
        raise SpamAssassinDiscard
    elif score > HOLD_SCORE:
        Hold.hold_for_approval(mlist, msg, msgdata,
                               SpamAssassinHold(score, symbols))
示例#43
0
文件: Utils.py 项目: Koumbit/mailman2
def IsDMARCProhibited(mlist, email):
    if not dns_resolver:
        # This is a problem; log it.
        syslog('error',
            'DNS lookup for dmarc_moderation_action for list %s not available',
            mlist.real_name)
        return False

    email = email.lower()
    # Scan from the right in case quoted local part has an '@'.
    at_sign = email.rfind('@')
    if at_sign < 1:
        return False
    f_dom = email[at_sign+1:]
    x = _DMARCProhibited(mlist, email, '_dmarc.' + f_dom)
    if x != 'continue':
        return x
    o_dom = get_org_dom(f_dom)
    if o_dom != f_dom:
        x = _DMARCProhibited(mlist, email, '_dmarc.' + o_dom, org=True)
        if x != 'continue':
            return x
    return False
示例#44
0
def matches_p(sender, nonmembers, listname):
    # First strip out all the regular expressions and listnames
    plainaddrs = [
        addr for addr in nonmembers
        if not (addr.startswith('^') or addr.startswith('@'))
    ]
    addrdict = Utils.List2Dict(plainaddrs, foldcase=1)
    if addrdict.has_key(sender):
        return 1
    # Now do the regular expression matches
    for are in nonmembers:
        if are.startswith('^'):
            try:
                cre = re.compile(are, re.IGNORECASE)
            except re.error:
                continue
            if cre.search(sender):
                return 1
        elif are.startswith('@'):
            # XXX Needs to be reviewed for list@domain names.
            try:
                mname = are[1:].lower().strip()
                if mname == listname:
                    # don't reference your own list
                    syslog('error',
                           '*_these_nonmembers in %s references own list',
                           listname)
                else:
                    mother = MailList(mname, lock=0)
                    if mother.isMember(sender):
                        return 1
            except Errors.MMUnknownListError:
                syslog(
                    'error',
                    '*_these_nonmembers in %s references non-existent list %s',
                    listname, mname)
    return 0
示例#45
0
def quick_maketext(templatefile, dict=None, lang=None, mlist=None):
    if mlist is None:
        listname = ''
    else:
        listname = mlist._internal_name
    if lang is None:
        if mlist is None:
            lang = mm_cfg.DEFAULT_SERVER_LANGUAGE
        else:
            lang = mlist.preferred_language
    cachekey = (templatefile, lang, listname)
    filepath = _templatefilepathcache.get(cachekey)
    if filepath:
        template = _templatecache.get(filepath)
    if filepath is None or template is None:
        # Use the basic maketext, with defaults to get the raw template
        template, filepath = Utils.findtext(templatefile,
                                            lang=lang,
                                            raw=True,
                                            mlist=mlist)
        _templatefilepathcache[cachekey] = filepath
        _templatecache[filepath] = template
    # Copied from Utils.maketext()
    text = template
    if dict is not None:
        try:
            sdict = SafeDict(dict)
            try:
                text = sdict.interpolate(template)
            except UnicodeError:
                # Try again after coercing the template to unicode
                utemplate = unicode(template, Utils.GetCharSet(lang),
                                    'replace')
                text = sdict.interpolate(utemplate)
        except (TypeError, ValueError), e:
            # The template is really screwed up
            syslog('error', 'broken template: %s\n%s', filepath, e)
示例#46
0
    def __init__(self, mlist):
        self.__mlist = mlist
        self._dbconnect()

        # define the table and standard condition reflecting listname
        # (this is for upwards compatibility with the 'wide' mode and
        # the formerly fixed name of database table in 'flat' mode)
        if self.getTableType() is 'flat':
            try:
                self._table = mm_cfg.MYSQL_MEMBER_TABLE_NAME
            except AttributeError:
                self._table = 'mailman_mysql'
            self._where = "listname = '%s'" %(self.__mlist.internal_name())
        else:
            self._table = self.__mlist.internal_name()
            self._where = '1=1'

        # Make sure we always have the table we need...
        # if mm_cfg.MYSQL_MEMBER_CREATE_TABLE was defined
        try:
            if mm_cfg.MYSQL_MEMBER_CREATE_TABLE:
                self.createTable()
        except AttributeError:
            pass

        if mm_cfg.MYSQL_MEMBER_DB_VERBOSE:
            # Message to indicate successful init.
            message = "MysqlMemberships " \
                + "$Revision: 1.69 $ initialized with host: %s (%s)" % (
                mm_cfg.connection.get_host_info(),
                mm_cfg.connection.get_server_info() )
            syslog('error', message)
            syslog('mysql', message)

        # add a cache memory
        self._cache = {}
        self._cachedate = 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)
示例#48
0
def process(mlist, msg, msgdata):
    # Short circuit non-digestable lists.
    if not mlist.digestable or msgdata.get('isdigest'):
        return
    mboxfile = os.path.join(mlist.fullpath(), 'digest.mbox')
    omask = os.umask(007)
    try:
        mboxfp = open(mboxfile, 'a+')
    finally:
        os.umask(omask)
    mbox = Mailbox(mboxfp)
    mbox.AppendMessage(msg)
    # Calculate the current size of the accumulation file.  This will not tell
    # us exactly how big the MIME, rfc1153, or any other generated digest
    # message will be, but it's the most easily available metric to decide
    # whether the size threshold has been reached.
    mboxfp.flush()
    size = os.path.getsize(mboxfile)
    if (mlist.digest_size_threshhold > 0
            and size / 1024.0 >= mlist.digest_size_threshhold):
        # This is a bit of a kludge to get the mbox file moved to the digest
        # queue directory.
        try:
            # Enclose in try/except here because a error in send_digest() can
            # silently stop regular delivery.  Unsuccessful digest delivery
            # should be tried again by cron and the site administrator will be
            # notified of any error explicitly by the cron error message.
            mboxfp.seek(0)
            send_digests(mlist, mboxfp)
            os.unlink(mboxfile)
        except Exception, errmsg:
            # Bare except is generally prohibited in Mailman, but we can't
            # forecast what exceptions can occur here.
            syslog('error', 'send_digests() failed: %s', errmsg)
            s = StringIO()
            traceback.print_exc(file=s)
            syslog('error', s.getvalue())
示例#49
0
    def _dbconnect(self):
        if mm_cfg.connection:
            try:
                if mm_cfg.connection.ping() == 0:
                    return mm_cfg.connection
            except:
                syslog('mysql', 'connection warning')
                pass

            # Connection failed, or an error, try a hard dis+reconnect.
            try:
                mm_cfg.cursor.close()
            except:
                syslog('error', 'error on mm_cfg.cursor.close()')
                pass

            try:
                mm_cfg.connection.close()
            except:
                syslog('error', 'error on mm_cfg.connection.close()')
                pass

        try:
            mm_cfg.connection = MySQLdb.connect(
                passwd=mm_cfg.MYSQL_MEMBER_DB_PASS,
                db=mm_cfg.MYSQL_MEMBER_DB_NAME,
                user=mm_cfg.MYSQL_MEMBER_DB_USER,
                host=mm_cfg.MYSQL_MEMBER_DB_HOST)
            mm_cfg.cursor = mm_cfg.connection.cursor()
        except MySQLdb.OperationalError, e:
            message = "Error connecting to MySQL database %s (%s): %s" % (
                mm_cfg.MYSQL_MEMBER_DB_NAME, e.args[0], e.args[1])
            syslog('error', message)
            if mm_cfg.MYSQL_MEMBER_DB_VERBOSE:
                syslog('mysql', message)
            # exit? why not sleep(30) and retry?
            sys.exit(1)
示例#50
0
    def SendHostileSubscriptionNotice(self, listname, address):
        # Some one was invited to one list but tried to confirm to a different
        # list.  We inform both list owners of the bogosity, but be careful
        # not to reveal too much information.
        selfname = self.internal_name()
        syslog('mischief', '%s was invited to %s but confirmed to %s', address,
               listname, selfname)
        # First send a notice to the attacked list
        msg = Message.OwnerNotification(
            self, _('Hostile subscription attempt detected'),
            Utils.wrap(
                _("""%(address)s was invited to a different mailing
list, but in a deliberate malicious attempt they tried to confirm the
invitation to your list.  We just thought you'd like to know.  No further
action by you is required.""")))
        msg.send(self)
        # Now send a notice to the invitee list
        try:
            # Avoid import loops
            from Mailman.MailList import MailList
            mlist = MailList(listname, lock=False)
        except Errors.MMListError:
            # Oh well
            return
        otrans = i18n.get_translation()
        i18n.set_language(mlist.preferred_language)
        try:
            msg = Message.OwnerNotification(
                mlist, _('Hostile subscription attempt detected'),
                Utils.wrap(
                    _("""You invited %(address)s to your list, but in a
deliberate malicious attempt, they tried to confirm the invitation to a
different list.  We just thought you'd like to know.  No further action by you
is required.""")))
            msg.send(mlist)
        finally:
            i18n.set_translation(otrans)
示例#51
0
def to_plaintext(msg):
    changedp = 0
    for subpart in typed_subpart_iterator(msg, 'text', 'html'):
        filename = tempfile.mktemp('.html')
        fp = open(filename, 'w')
        try:
            fp.write(subpart.get_payload(decode=1))
            fp.close()
            cmd = os.popen(mm_cfg.HTML_TO_PLAIN_TEXT_COMMAND %
                           {'filename': filename})
            plaintext = cmd.read()
            rtn = cmd.close()
            if rtn:
                syslog('error', 'HTML->text/plain error: %s', rtn)
        finally:
            try:
                os.unlink(filename)
            except OSError, e:
                if e.errno <> errno.ENOENT: raise
        # Now replace the payload of the subpart and twiddle the Content-Type:
        del subpart['content-transfer-encoding']
        subpart.set_payload(plaintext)
        subpart.set_type('text/plain')
        changedp = 1
示例#52
0
def main():
    doc = Document()
    doc.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE)

    parts = Utils.GetPathPieces()
    if not parts or len(parts) < 1:
        bad_confirmation(doc)
        doc.AddItem(MailmanLogo())
        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)
        bad_confirmation(doc, _('No such list <em>%(safelistname)s</em>'))
        doc.AddItem(MailmanLogo())
        # Send this with a 404 status.
        print 'Status: 404 Not Found'
        print doc.Format()
        syslog('error', 'confirm: No such list "%s": %s', listname, e)
        return
示例#53
0
    def __init__(self, slice=None, numslices=None):
        """Create a faux runner which checks into Launchpad occasionally.

        Every XMLRPC_SLEEPTIME number of seconds, this runner wakes up and
        connects to a Launchpad XMLRPC service to see if there's anything for
        it to do.  slice and numslices are ignored, but required by the
        Mailman queue runner framework.
        """
        self.SLEEPTIME = mm_cfg.XMLRPC_SLEEPTIME
        # Instead of calling the superclass's __init__() method, just
        # initialize the two attributes that are actually used.  The reason
        # for this is that the XMLRPCRunner doesn't have a queue so it
        # shouldn't be trying to create a Switchboard instance.  Still, it
        # needs a dummy _kids and _stop attributes for the rest of the runner
        # to work.  We're using runners in a more general sense than Mailman 2
        # is designed for.
        self._kids = {}
        self._stop = False
        self._proxy = get_mailing_list_api_proxy()
        # Ensure that the log file exists, mostly for the test suite.
        syslog('xmlrpc', 'XMLRPC runner starting')
        self.heartbeat_frequency = timedelta(minutes=5)
        self.last_heartbeat = None
        self._heartbeat()
示例#54
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(_('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
示例#55
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
示例#56
0
def decorate(mlist, template, what, extradict=None):
    # `what' is just a descriptive phrase used in the log message
    
    # If template is only whitespace, ignore it.
    if len(re.sub('\s', '', template)) == 0:
        return ''

    # BAW: We've found too many situations where Python can be fooled into
    # interpolating too much revealing data into a format string.  For
    # example, a footer of "% silly %(real_name)s" would give a header
    # containing all list attributes.  While we've previously removed such
    # really bad ones like `password' and `passwords', it's much better to
    # provide a whitelist of known good attributes, then to try to remove a
    # blacklist of known bad ones.
    d = SafeDict({'real_name'     : mlist.real_name,
                  'list_name'     : mlist.internal_name(),
                  # For backwards compatibility
                  '_internal_name': mlist.internal_name(),
                  'host_name'     : mlist.host_name,
                  'web_page_url'  : mlist.web_page_url,
                  'description'   : mlist.description,
                  'info'          : mlist.info,
                  'cgiext'        : mm_cfg.CGIEXT,
                  })
    if extradict is not None:
        d.update(extradict)
    # Using $-strings?
    if getattr(mlist, 'use_dollar_strings', 0):
        template = Utils.to_percent(template)
    # Interpolate into the template
    try:
        text = re.sub(r'(?m)(?<!^--) +(?=\n)', '',
                      re.sub(r'\r\n', r'\n', template % d))
    except (ValueError, TypeError), e:
        syslog('error', 'Exception while calculating %s:\n%s', what, e)
        text = template
示例#57
0
 def _dispose(self, mlist, msg, msgdata):
     # The policy here is similar to the Replybot policy.  If a message has
     # "Precedence: bulk|junk|list" and no "X-Ack: yes" header, we discard
     # it to prevent replybot response storms.
     precedence = msg.get('precedence', '').lower()
     ack = msg.get('x-ack', '').lower()
     if ack <> 'yes' and precedence in ('bulk', 'junk', 'list'):
         syslog('vette', 'Precedence: %s message discarded by: %s',
                precedence, mlist.GetRequestEmail())
         return False
     # Do replybot for commands
     mlist.Load()
     Replybot.process(mlist, msg, msgdata)
     if mlist.autorespond_requests == 1:
         syslog('vette', 'replied and discard')
         # w/discard
         return False
     # Now craft the response
     res = Results(mlist, msg, msgdata)
     # BAW: Not all the functions of this qrunner require the list to be
     # locked.  Still, it's more convenient to lock it here and now and
     # deal with lock failures in one place.
     try:
         mlist.Lock(timeout=mm_cfg.LIST_LOCK_TIMEOUT)
     except LockFile.TimeOutError:
         # Oh well, try again later
         return True
     # This message will have been delivered to one of mylist-request,
     # mylist-join, or mylist-leave, and the message metadata will contain
     # a key to which one was used.
     try:
         ret = BADCMD
         if msgdata.get('torequest'):
             ret = res.process()
         elif msgdata.get('tojoin'):
             ret = res.do_command('join')
         elif msgdata.get('toleave'):
             ret = res.do_command('leave')
         elif msgdata.get('toconfirm'):
             mo = re.match(mm_cfg.VERP_CONFIRM_REGEXP, msg.get('to', ''))
             if mo:
                 ret = res.do_command('confirm', (mo.group('cookie'), ))
         if ret == BADCMD and mm_cfg.DISCARD_MESSAGE_WITH_NO_COMMAND:
             syslog('vette', 'No command, message discarded, msgid: %s',
                    msg.get('message-id', 'n/a'))
         else:
             res.send_response()
             mlist.Save()
     finally:
         mlist.Unlock()
 def _dopipeline(self, mlist, msg, msgdata, pipeline):
     while pipeline:
         handler = pipeline.pop(0)
         modname = 'Mailman.Handlers.' + handler
         __import__(modname)
         try:
             pid = os.getpid()
             sys.modules[modname].process(mlist, msg, msgdata)
             # Failsafe -- a child may have leaked through.
             if pid != os.getpid():
                 syslog('error', 'child process leaked thru: %s', modname)
                 os._exit(1)
         except Errors.DiscardMessage:
             # Throw the message away; we need do nothing else with it.
             # We do need to push the current handler back in the pipeline
             # just in case the syslog call throws an exception and the
             # message is shunted.
             pipeline.insert(0, handler)
             syslog(
                 'vette', """Message discarded, msgid: %s'
     list: %s,
     handler: %s""", msg.get('message-id', 'n/a'), mlist.real_name, handler)
             return 0
         except Errors.HoldMessage:
             # Let the approval process take it from here.  The message no
             # longer needs to be queued.
             return 0
         except Errors.RejectMessage as e:
             # Log this.
             # We do need to push the current handler back in the pipeline
             # just in case the syslog call or BounceMessage throws an
             # exception and the message is shunted.
             pipeline.insert(0, handler)
             syslog(
                 'vette', """Message rejected, msgid: %s
     list: %s,
     handler: %s,
     reason: %s""", msg.get('message-id', 'n/a'), mlist.real_name, handler,
                 e.notice())
             mlist.BounceMessage(msg, msgdata, e)
             return 0
         except:
             # Push this pipeline module back on the stack, then re-raise
             # the exception.
             pipeline.insert(0, handler)
             raise
     # We've successfully completed handling of this message
     return 0
示例#59
0
def do_exclude(mlist, msg, msgdata, recips):
    # regular_exclude_lists are the other mailing lists on this mailman
    # installation whose members are excluded from the regular (non-digest)
    # delivery of this list if those list addresses appear in To: or Cc:
    # headers.
    if not mlist.regular_exclude_lists:
        return recips
    recips = set(recips)
    destinations = email.Utils.getaddresses(msg.get_all('to', []) +
                                            msg.get_all('cc', []))
    destinations = [y.lower() for x,y in destinations]
    for listname in mlist.regular_exclude_lists:
        listname = listname.lower()
        if listname not in destinations:
            continue
        listlhs, hostname = listname.split('@')
        if listlhs == mlist.internal_name():
            syslog('error', 'Exclude list %s is a self reference.',
                    listname)
            continue
        try:
            slist = MailList(listlhs, lock=False)
        except MMUnknownListError:
            syslog('error', 'Exclude list %s not found.', listname)
            continue
        if not mm_cfg.ALLOW_CROSS_DOMAIN_SIBLING \
           and slist.host_name != hostname:
            syslog('error', 'Exclude list %s is not in the same domain.',
                    listname)
            continue
        if mlist.regular_exclude_ignore:
            for sender in msg.get_senders():
                if slist.isMember(sender):
                    break
                for sender in Utils.check_eq_domains(sender,
                                  slist.equivalent_domains):
                    if slist.isMember(sender):
                        break
                if slist.isMember(sender):
                    break
            else:
                continue
        srecips = set([slist.getMemberCPAddress(m)
                   for m in slist.getRegularMemberKeys()
                   if slist.getDeliveryStatus(m) == ENABLED])
        recips -= srecips
    return list(recips)
示例#60
0
def log_exception(message, *args):
    """Write the current exception traceback into the Mailman log file.

    This is really just a convenience function for a refactored chunk of
    common code.

    :param message: The message to appear in the xmlrpc and error logs. It
        may be a format string.
    :param args: Optional arguments to be interpolated into a format string.
    """
    error_utility = ErrorReportingUtility()
    error_utility.configure(section_name='mailman')
    error_utility.raising(sys.exc_info())
    out_file = StringIO()
    traceback.print_exc(file=out_file)
    traceback_text = out_file.getvalue()
    syslog('xmlrpc', message, *args)
    syslog('error', message, *args)
    syslog('error', traceback_text)