Commit f4a456a8 authored by bwarsaw's avatar bwarsaw

Merged revisions 8113-8121 via svnmerge from

https://mailman.svn.sourceforge.net/svnroot/mailman/branches/tmp-sqlalchemy-branch

................
  r8114 | bwarsaw | 2006-12-06 00:16:54 -0500 (Wed, 06 Dec 2006) | 44 lines
  
  Initial take on using SQLAlchemy to store list data in lieu of Python pickles.
  While all the list data (including OldStyleMemberships attributes) are stored
  in the database, many attributes are stored as PickleTypes binary data.  This
  isn't idea but it gets things working until a more sophisticated schema can be
  developed.
  
  MailList class is now a new-style class, as is required by SQLAlchemy.  This
  makes several things, er, interesting.  Rip out all the low-level pickle
  reading and writing stuff.  Hook SA transaction events into Lock() and
  Unlock().  Move the hooking of the _memberadaptor into InitTempVars(), which
  gets called by the SQLAlchemy hooks (MailList.__init__() never is).
  
  Add an initialize.py module which centralizes all the initialization bits that
  command line scripts have to do, including configuration, logging, and atabase
  initialization.
  
  This change also converts bin/withlist to mmshell wrapper.
  
  Update to SQLAlchemy 0.3.1.
  
  Revamp paths.py.in considerably.  There were several problems with the old
  way.  We no longer disable default loading of site-packages so we don't need
  to add Python's site-packages back to sys.path.  Also, because
  site.addsitedir() causes things like .pth paths to be /appended/ to sys.path,
  they actually won't override any site-installed packages.  E.g. if SQLAlchemy
  is installed in the system Python, our version will not override.  IIUC,
  setuptools-based packages can be configured to work properly in the face of
  package versions, however not all packages we currently depend on are
  setuptools-based.  So instead, we steal a bit of stuff from site.py but change
  things so the prepend .pth stuff to sys.path.
  
  Update several modules to use True/False and whitespace normalization.
  Convert from mm_cfg to config object.  Modernize a few coding constructs.
  
  Add a couple of exceptions to handle database problems.
  
  In the export script, include the widget type in the elements.  This helped in
  my stupid little throw away conversion script, but I think it will be more
  generally useful.
  
  Add an interact.py module which refactors interactive interpreter access.
  Mostly this is used by withlist -i, but it lets us import Mailman.interact and
  drop into a prompt just about anywhere (e.g. debugging).
................
  r8115 | bwarsaw | 2006-12-07 09:13:56 -0500 (Thu, 07 Dec 2006) | 22 lines
  
  Start to flesh out more of the SQLAlchemy mechanisms.
  
  Added a MailList.__new__() which hooks instantiation to use a query on
  dbcontext to get an existing mailing list.  A 'no-args' call means we're doing
  a Create(), though eventually that will change too.
  
  For now, disable the CheckVersion() call.  Eventually this will be folded into
  schema migration.
  
  list_exists(): Rewrite to use the dbcontext query to determine if the named
  mailing list exists or not.  Requires the fqdn_listname.
  
  Eradicate two failed member adaptors: BDBMemberAdaptor and SAMemberships.
  
  Change the way the DBContext holds onto tables.  It now keeps a dictionary
  mapping the table's name to the SA Table instance.  This makes it easier to
  look up and use the individual tables.
  
  Add 'web_page_url' as an attribute managed by SA, and remove a debugging
  print.
................
  r8116 | bwarsaw | 2006-12-11 07:27:47 -0500 (Mon, 11 Dec 2006) | 29 lines
  
  Rework the whole dbcontext and transaction framework.  SA already handles
  nested transactions so we don't have to worry about them.  However, we do have
  the weird situation where some transactions are tied to MailList
  .Lock()/.Unlock()/.Save() and some are tied to non-mlist actions.  So now we
  use an @txn decorator to put methods in a session transaction, but then we
  also hook into the above MailList methods as possibly sub-transactions.  We
  use a weakref subclass to manage the MailList interface, with a dictionary
  mapping MailList fqdn_listnames against transactions.  The weakrefs come in by
  giving us a callback when a MailList gets derefed such that we're guaranteed
  to rollback any outstanding transaction.
  
  Also, we have one global DBContext instance but rather than force the rest of
  Mailman to deal with context objects, instead we expose API methods on that
  object into the Mailman.database module, which the rest of the code will use.
  Such methods must be prepended with 'api_' to get exposed this way.
  
  bin/rmlist now works with the SA-backend.  I refactored the code here so that
  other code (namely, the test suite) can more easily and consistently remove a
  mailing list.  This isn't the best place for it ultimately, but it's good
  enough for now.
  
  New convenience functions Utils.split_listname(), .fqdn_listname().
  
  Convert testall to use Mailman.initialize.initialize().  Not all tests work,
  but I'm down to only 8 failures and 7 errors.  Also, do a better job of
  recovering from failures in setUp().
  
  MailList.__new__() now takes keyword arguments.
................
  r8117 | bwarsaw | 2006-12-11 22:58:06 -0500 (Mon, 11 Dec 2006) | 7 lines
  
  Unit test repairs; even though the unit tests are still pretty fragile,
  everything now passes with the SQLAlchemy storage of list data.
  
  Added missing 'personalize' column.  Converted mailmanctl and qrunner to
  initialize() interface.  Fixed _cookie_path() to not fail if SCRIPT_NAME is
  not in the environment.
................
  r8118 | bwarsaw | 2006-12-27 18:45:41 -0500 (Wed, 27 Dec 2006) | 21 lines
  
  Utils.list_names(): Use a database query to get all the list names.
  
  dbcontext.py: Added api_get_list_names() to support Utils.list_names().
  
  listdata.py: Added two additional MailList attributes which need to be stored
  in the database.  The first is 'admin_member_chunksize' which isn't modifiable
  from the web.  The second is 'password' which holds the list's password.
  
  HTMLFormatObject: item strings can now be unicodes.
  
  bin/list_lists.py: Must call initialize() to get the database properly
  initialized, not just config.load().  This will be a common theme.
  
  SecurityManager.py:
      - Remove md5 and crypt support
      - Added mailman.debug logger, though it will be only used during
        debugging.
      - The 'secret' can be a unicode now.
      - A few coding style updates; repr() instead of backticks, 'key in dict'
        instead of 'dict.has_key(key)'
................
  r8119 | bwarsaw | 2006-12-27 19:13:09 -0500 (Wed, 27 Dec 2006) | 2 lines
  
  genaliases.py: config.load() -> initialize()
................
  r8120 | bwarsaw | 2006-12-27 19:17:26 -0500 (Wed, 27 Dec 2006) | 9 lines
  
  Blocked revisions 8113 via svnmerge
  
  ........
    r8113 | bwarsaw | 2006-12-05 23:54:30 -0500 (Tue, 05 Dec 2006) | 3 lines
    
    Initialized merge tracking via "svnmerge" with revisions "1-8112" from 
    https://mailman.svn.sourceforge.net/svnroot/mailman/branches/tmp-sqlalchemy-branch
  ........
................
  r8121 | bwarsaw | 2006-12-28 23:34:52 -0500 (Thu, 28 Dec 2006) | 20 lines
  
  Remove SIGTERM handling from all the CGI scripts.  This messes with HTTPRunner
  because when you issue "mailmanctl stop" after the signal handler has been
  installed, the process will get a SIGTERM, the signal handler will run, and
  the process will exit with a normal zero code.  This will cause mailmanctl to
  try to restart the HTTPRunner.
  
  I don't think we need that stuff at all when running under wsgi with a
  SQLAlchemy backend.  If mailmanctl kills the HTTPRunner in the middle of the
  process, I believe (but have not tested) that the transaction should get
  properly rolled back at process exit.  We need to make sure about this, and
  also we need to test the signal handling functionality under traditional CGI
  environment (if we even still want to support that).
  
  Also, make sure that we don't try to initialize the loggers twice in qrunner.
  This was the cause of all the double entries in logs/qrunner.
  
  Fix a coding style nit in mailmanctl.py.
  
  De-DOS-ify line endings in loginit.py.
................
parent ae185106
# Copyright (C) 1998,1999,2000,2001,2002 by the Free Software Foundation, Inc.
# Copyright (C) 1998-2006 by the Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
# USA.
"""MailList mixin class managing the autoresponder.
"""
from Mailman import mm_cfg
from Mailman.i18n import _
"""MailList mixin class managing the autoresponder."""
class Autoresponder:
def InitVars(self):
# configurable
self.autorespond_postings = 0
self.autorespond_admin = 0
self.autorespond_postings = False
self.autorespond_admin = False
# this value can be
# 0 - no autoresponse on the -request line
# 1 - autorespond, but discard the original message
......@@ -40,4 +37,3 @@ class Autoresponder:
self.postings_responses = {}
self.admin_responses = {}
self.request_responses = {}
This diff is collapsed.
......@@ -24,19 +24,20 @@ import logging
from email.MIMEMessage import MIMEMessage
from email.MIMEText import MIMEText
from Mailman import Defaults
from Mailman import MemberAdaptor
from Mailman import Message
from Mailman import Pending
from Mailman import Utils
from Mailman import i18n
from Mailman import mm_cfg
from Mailman.configuration import config
EMPTYSTRING = ''
# This constant is supposed to represent the day containing the first midnight
# after the epoch. We'll add (0,)*6 to this tuple to get a value appropriate
# for time.mktime().
ZEROHOUR_PLUSONEDAY = time.localtime(mm_cfg.days(1))[:3]
ZEROHOUR_PLUSONEDAY = time.localtime(Defaults.days(1))[:3]
def _(s): return s
......@@ -81,19 +82,19 @@ class _BounceInfo:
class Bouncer:
def InitVars(self):
# Configurable...
self.bounce_processing = mm_cfg.DEFAULT_BOUNCE_PROCESSING
self.bounce_score_threshold = mm_cfg.DEFAULT_BOUNCE_SCORE_THRESHOLD
self.bounce_info_stale_after = mm_cfg.DEFAULT_BOUNCE_INFO_STALE_AFTER
self.bounce_processing = config.DEFAULT_BOUNCE_PROCESSING
self.bounce_score_threshold = config.DEFAULT_BOUNCE_SCORE_THRESHOLD
self.bounce_info_stale_after = config.DEFAULT_BOUNCE_INFO_STALE_AFTER
self.bounce_you_are_disabled_warnings = \
mm_cfg.DEFAULT_BOUNCE_YOU_ARE_DISABLED_WARNINGS
config.DEFAULT_BOUNCE_YOU_ARE_DISABLED_WARNINGS
self.bounce_you_are_disabled_warnings_interval = \
mm_cfg.DEFAULT_BOUNCE_YOU_ARE_DISABLED_WARNINGS_INTERVAL
config.DEFAULT_BOUNCE_YOU_ARE_DISABLED_WARNINGS_INTERVAL
self.bounce_unrecognized_goes_to_list_owner = \
mm_cfg.DEFAULT_BOUNCE_UNRECOGNIZED_GOES_TO_LIST_OWNER
config.DEFAULT_BOUNCE_UNRECOGNIZED_GOES_TO_LIST_OWNER
self.bounce_notify_owner_on_disable = \
mm_cfg.DEFAULT_BOUNCE_NOTIFY_OWNER_ON_DISABLE
config.DEFAULT_BOUNCE_NOTIFY_OWNER_ON_DISABLE
self.bounce_notify_owner_on_removal = \
mm_cfg.DEFAULT_BOUNCE_NOTIFY_OWNER_ON_REMOVAL
config.DEFAULT_BOUNCE_NOTIFY_OWNER_ON_REMOVAL
# Not configurable...
#
# This holds legacy member related information. It's keyed by the
......@@ -153,7 +154,7 @@ class Bouncer:
# Now that we've adjusted the bounce score for this bounce, let's
# check to see if the disable-by-bounce threshold has been reached.
if info.score >= self.bounce_score_threshold:
if mm_cfg.VERP_PROBES:
if config.VERP_PROBES:
log.info('sending %s list probe to: %s (score %s >= %s)',
self.internal_name(), member, info.score,
self.bounce_score_threshold)
......@@ -168,7 +169,7 @@ class Bouncer:
cookie = self.pend_new(Pending.RE_ENABLE, self.internal_name(), member)
info.cookie = cookie
# Disable them
if mm_cfg.VERP_PROBES:
if config.VERP_PROBES:
log.info('%s: %s disabling due to probe bounce received',
self.internal_name(), member)
else:
......
......@@ -22,7 +22,6 @@ import re
import cgi
import sha
import sys
import signal
import urllib
import logging
......@@ -127,40 +126,8 @@ def main():
# The html page document
doc = Document()
doc.set_language(mlist.preferred_language)
# From this point on, the MailList object must be locked. However, we
# must release the lock no matter how we exit. try/finally isn't enough,
# because of this scenario: user hits the admin page which may take a long
# time to render; user gets bored and hits the browser's STOP button;
# browser shuts down socket; server tries to write to broken socket and
# gets a SIGPIPE. Under Apache 1.3/mod_cgi, Apache catches this SIGPIPE
# (I presume it is buffering output from the cgi script), then turns
# around and SIGTERMs the cgi process. Apache waits three seconds and
# then SIGKILLs the cgi process. We /must/ catch the SIGTERM and do the
# most reasonable thing we can in as short a time period as possible. If
# we get the SIGKILL we're screwed (because it's uncatchable and we'll
# have no opportunity to clean up after ourselves).
#
# This signal handler catches the SIGTERM, unlocks the list, and then
# exits the process. The effect of this is that the changes made to the
# MailList object will be aborted, which seems like the only sensible
# semantics.
#
# BAW: This may not be portable to other web servers or cgi execution
# models.
def sigterm_handler(signum, frame, mlist=mlist):
# Make sure the list gets unlocked...
mlist.Unlock()
# ...and ensure we exit, otherwise race conditions could cause us to
# enter MailList.Save() while we're in the unlocked state, and that
# could be bad!
sys.exit(0)
mlist.Lock()
try:
# Install the emergency shutdown signal handler
signal.signal(signal.SIGTERM, sigterm_handler)
if cgidata.keys():
# There are options to change
change_options(mlist, category, subcat, cgidata, doc)
......@@ -190,10 +157,6 @@ def main():
print doc.Format()
mlist.Save()
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.
mlist.Unlock()
......
......@@ -23,7 +23,6 @@ import sys
import time
import email
import errno
import signal
import logging
from urllib import quote_plus, unquote_plus
......@@ -134,27 +133,8 @@ def main():
if qs and isinstance(qs, list):
details = qs[0]
# We need a signal handler to catch the SIGTERM that can come from Apache
# when the user hits the browser's STOP button. See the comment in
# admin.py for details.
#
# BAW: Strictly speaking, the list should not need to be locked just to
# read the request database. However the request database asserts that
# the list is locked in order to load it and it's not worth complicating
# that logic.
def sigterm_handler(signum, frame, mlist=mlist):
# Make sure the list gets unlocked...
mlist.Unlock()
# ...and ensure we exit, otherwise race conditions could cause us to
# enter MailList.Save() while we're in the unlocked state, and that
# could be bad!
sys.exit(0)
mlist.Lock()
try:
# Install the emergency shutdown signal handler
signal.signal(signal.SIGTERM, sigterm_handler)
realname = mlist.real_name
if not cgidata.keys():
# If this is not a form submission (i.e. there are no keys in the
......
......@@ -21,7 +21,6 @@ import os
import cgi
import sha
import sys
import signal
import logging
from Mailman import Errors
......@@ -161,18 +160,7 @@ def process_request(doc, cgidata):
fqdn_listname = '%[email protected]%s' % (listname, email_host)
# We've got all the data we need, so go ahead and try to create the list
mlist = MailList.MailList()
# See admin.py for why we need to set up the signal handler.
def sigterm_handler(signum, frame):
# Make sure the list gets unlocked...
mlist.Unlock()
# ...and ensure we exit, otherwise race conditions could cause us to
# enter MailList.Save() while we're in the unlocked state, and that
# could be bad!
sys.exit(0)
try:
# Install the emergency shutdown signal handler
signal.signal(signal.SIGTERM, sigterm_handler)
pw = sha.new(password).hexdigest()
# Guarantee that all newly created files have the proper permission.
# proper group ownership should be assured by the autoconf script
......@@ -205,10 +193,6 @@ def process_request(doc, cgidata):
mlist.default_member_moderation = moderate
mlist.Save()
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.
mlist.Unlock()
# Now do the MTA-specific list creation tasks
if config.MTA:
......
......@@ -20,7 +20,6 @@
import os
import cgi
import sys
import signal
import urllib
import logging
......@@ -369,13 +368,6 @@ address. Upon confirmation, any other mailing list containing the address
_('Addresses may not be blank'))
print doc.Format()
return
# Standard sigterm handler.
def sigterm_handler(signum, frame, mlist=mlist):
mlist.Unlock()
sys.exit(0)
signal.signal(signal.SIGTERM, sigterm_handler)
if set_address:
if cpuser is None:
cpuser = user
......@@ -463,15 +455,6 @@ address. Upon confirmation, any other mailing list containing the address
print doc.Format()
return
# Standard signal handler
def sigterm_handler(signum, frame, mlist=mlist):
mlist.Unlock()
sys.exit(0)
# Okay, zap them. Leave them sitting at the list's listinfo page. We
# must own the list lock, and we want to make sure the user (BAW: and
# list admin?) is informed of the removal.
signal.signal(signal.SIGTERM, sigterm_handler)
mlist.Lock()
needapproval = False
try:
......@@ -582,7 +565,6 @@ address. Upon confirmation, any other mailing list containing the address
# Now, lock the list and perform the changes
mlist.Lock()
try:
signal.signal(signal.SIGTERM, sigterm_handler)
# `values' is a tuple of flags and the web values
for flag, newval in newvals:
# Handle language settings differently
......@@ -926,23 +908,10 @@ def lists_of_member(mlist, user):
def change_password(mlist, user, newpw, confirmpw):
# This operation requires the list lock, so let's set up the signal
# handling so the list lock will get released when the user hits the
# browser stop button.
def sigterm_handler(signum, frame, mlist=mlist):
# Make sure the list gets unlocked...
mlist.Unlock()
# ...and ensure we exit, otherwise race conditions could cause us to
# enter MailList.Save() while we're in the unlocked state, and that
# could be bad!
sys.exit(0)
# Must own the list lock!
mlist.Lock()
try:
# Install the emergency shutdown signal handler
signal.signal(signal.SIGTERM, sigterm_handler)
# change the user's password. The password must already have been
# Change the user's password. The password must already have been
# compared to the confirmpw and otherwise been vetted for
# acceptability.
mlist.setMemberPassword(user, newpw)
......@@ -973,9 +942,6 @@ def global_options(mlist, user, globalopts):
# Must own the list lock!
mlist.Lock()
try:
# Install the emergency shutdown signal handler
signal.signal(signal.SIGTERM, sigterm_handler)
if globalopts.enable is not None:
mlist.setDeliveryStatus(user, globalopts.enable)
......
......@@ -20,7 +20,6 @@
import os
import cgi
import sys
import signal
import logging
from Mailman import Errors
......@@ -76,27 +75,8 @@ def main():
i18n.set_language(language)
doc.set_language(language)
# We need a signal handler to catch the SIGTERM that can come from Apache
# when the user hits the browser's STOP button. See the comment in
# admin.py for details.
#
# BAW: Strictly speaking, the list should not need to be locked just to
# read the request database. However the request database asserts that
# the list is locked in order to load it and it's not worth complicating
# that logic.
def sigterm_handler(signum, frame, mlist=mlist):
# Make sure the list gets unlocked...
mlist.Unlock()
# ...and ensure we exit, otherwise race conditions could cause us to
# enter MailList.Save() while we're in the unlocked state, and that
# could be bad!
sys.exit(0)
mlist.Lock()
try:
# Install the emergency shutdown signal handler
signal.signal(signal.SIGTERM, sigterm_handler)
process_form(mlist, doc, cgidata, language)
mlist.Save()
finally:
......
......@@ -100,20 +100,18 @@ HTML_TO_PLAIN_TEXT_COMMAND = '/usr/bin/lynx -dump %(filename)s'
# Database options
#####
# Specify the name of the membership adaptor class that implements the
# MemberAdaptor interface you want to use. The first is traditional
# pickle-based adapter that was standard for Mailman 2.1 and earlier.
MEMBER_ADAPTOR_CLASS = 'Mailman.OldStyleMemberships.OldStyleMemberships'
# Use this to set the SQLAlchemy database engine URL. You generally have one
# primary database connection for all of Mailman. List data and most rosters
# will store their data in this database, although external rosters may access
# other databases in their own way. This string support substitutions using
# any variable in the Configuration object.
SQLALCHEMY_ENGINE_URL = 'sqlite:///$DATA_DIR/mailman.db'
# This is the SQLAlchemy-based adaptor which lets you store all membership
# data in any of several supported RDBMs.
#MEMBER_ADAPTOR_CLASS = 'Mailman.SAMemberships.SAMemberships'
# For debugging purposes
SQLALCHEMY_ECHO = False
# If you choose the SAMemberships adaptor, set this variable to specify the
# connection url to the backend database engine. Specify the placeholder
# $listdir for the directory that list data is stored in, and $listname for
# the name of the mailing list.
SQLALCHEMY_ENGINE_URL = 'sqlite:///$listdir/members.db'
# XXX REMOVE ME
MEMBER_ADAPTOR_CLASS = 'Mailman.OldStyleMemberships.OldStyleMemberships'
......
# Copyright (C) 1998,1999,2000,2001,2002 by the Free Software Foundation, Inc.
# Copyright (C) 1998-2006 by the Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
# USA.
"""Mixin class with list-digest handling methods and settings."""
import os
from stat import ST_SIZE
import errno
from Mailman import mm_cfg
from Mailman import Utils
from Mailman import Errors
from Mailman import Utils
from Mailman.Handlers import ToDigest
from Mailman.configuration import config
from Mailman.i18n import _
class Digester:
def InitVars(self):
# Configurable
self.digestable = mm_cfg.DEFAULT_DIGESTABLE
self.digest_is_default = mm_cfg.DEFAULT_DIGEST_IS_DEFAULT
self.mime_is_default_digest = mm_cfg.DEFAULT_MIME_IS_DEFAULT_DIGEST
self.digest_size_threshhold = mm_cfg.DEFAULT_DIGEST_SIZE_THRESHHOLD
self.digest_send_periodic = mm_cfg.DEFAULT_DIGEST_SEND_PERIODIC
self.next_post_number = 1
self.digest_header = mm_cfg.DEFAULT_DIGEST_HEADER
self.digest_footer = mm_cfg.DEFAULT_DIGEST_FOOTER
self.digest_volume_frequency = mm_cfg.DEFAULT_DIGEST_VOLUME_FREQUENCY
# Non-configurable.
# Configurable
self.digestable = config.DEFAULT_DIGESTABLE
self.digest_is_default = config.DEFAULT_DIGEST_IS_DEFAULT
self.mime_is_default_digest = config.DEFAULT_MIME_IS_DEFAULT_DIGEST
self.digest_size_threshhold = config.DEFAULT_DIGEST_SIZE_THRESHHOLD
self.digest_send_periodic = config.DEFAULT_DIGEST_SEND_PERIODIC
self.next_post_number = 1
self.digest_header = config.DEFAULT_DIGEST_HEADER
self.digest_footer = config.DEFAULT_DIGEST_FOOTER
self.digest_volume_frequency = config.DEFAULT_DIGEST_VOLUME_FREQUENCY
# Non-configurable.
self.one_last_digest = {}
self.digest_members = {}
self.next_digest_number = 1
self.digest_members = {}
self.next_digest_number = 1
self.digest_last_sent_at = 0
def send_digest_now(self):
......@@ -55,7 +55,7 @@ class Digester:
try:
mboxfp = None
# See if there's a digest pending for this mailing list
if os.stat(digestmbox)[ST_SIZE] > 0:
if os.stat(digestmbox).st_size > 0:
mboxfp = open(digestmbox)
ToDigest.send_digests(self, mboxfp)
os.unlink(digestmbox)
......@@ -63,10 +63,11 @@ class Digester:
if mboxfp:
mboxfp.close()
except OSError, e:
if e.errno <> errno.ENOENT: raise
if e.errno <> errno.ENOENT:
raise
# List has no outstanding digests
return 0
return 1
return False
return True
def bump_digest_volume(self):
self.volume += 1
......
......@@ -175,3 +175,19 @@ class HostileSubscriptionError(MailmanError):
"""A cross-subscription attempt was made."""
# This exception gets raised when an invitee attempts to use the
# invitation to cross-subscribe to some other mailing list.
# Database exceptions
class DatabaseError(MailmanError):
"""A problem with the database occurred."""
class SchemaVersionMismatchError(DatabaseError):
def __init__(self, got):
self._got = got
def __str__(self):
from Mailman.Version import DATABASE_SCHEMA_VERSION
return 'Incompatible database schema version (got: %d, expected: %d)' \
% (self._got, DATABASE_SCHEMA_VERSION)
# Copyright (C) 1998,1999,2000,2001,2002 by the Free Software Foundation, Inc.
# Copyright (C) 1998-2006 by the Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
......@@ -12,7 +12,8 @@
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
# USA.
"""Mixin class for configuring Usenet gateway.
......@@ -21,18 +22,18 @@ gateway and cron/gate_news for the news->mail gateway.
"""
from Mailman import mm_cfg
from Mailman.i18n import _
from Mailman.configuration import config
class GatewayManager:
def InitVars(self):
# Configurable
self.nntp_host = mm_cfg.DEFAULT_NNTP_HOST
self.nntp_host = config.DEFAULT_NNTP_HOST
self.linked_newsgroup = ''
self.gateway_to_news = 0
self.gateway_to_mail = 0
self.news_prefix_subject_too = 1
self.gateway_to_news = False
self.gateway_to_mail = False
self.news_prefix_subject_too = True
# In patch #401270, this was called newsgroup_is_moderated, but the
# semantics weren't quite the same.
self.news_moderation = 0
self.news_moderation = False
......@@ -284,7 +284,7 @@ def create(mlist, cgi=False, nolock=False, quiet=False):
if not nolock:
lock = makelock()
lock.lock()
# Do the aliases file, which need to be done in any case
# Do the aliases file, which always needs to be done
try:
if config.USE_LMTP:
_do_create(mlist, TRPTFILE, _addtransport)
......
This diff is collapsed.
......@@ -44,7 +44,7 @@ SHELL= /bin/sh
MODULES= $(srcdir)/*.py
SUBDIRS= Cgi Archiver Handlers Bouncers Queue MTA Gui Commands \
bin testing
bin database testing
# Modes for directories and executables created by the install
# process. Default to group-writable directories but
......
......@@ -12,7 +12,8 @@
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
# USA.
"""Track pending actions which require confirmation."""
......@@ -23,7 +24,8 @@ import errno
import random
import cPickle
from Mailman import mm_cfg
from Mailman.configuration import config
# Types of pending records
CHANGE_OF_ADDRESS = 'C'
......@@ -43,6 +45,7 @@ _ALLKEYS = (
)
_missing = object()
_default = object()
......@@ -54,7 +57,7 @@ class Pending:
"""Create a new entry in the pending database, returning cookie for it.
"""
assert op in _ALLKEYS, 'op: %s' % op
lifetime = kws.get('lifetime', mm_cfg.PENDING_REQUEST_LIFE)
lifetime = kws.get('lifetime', config.PENDING_REQUEST_LIFE)
# We try the main loop several times. If we get a lock error somewhere
# (for instance because someone broke the lock) we simply try again.
assert self.Locked()
......@@ -108,7 +111,7 @@ class Pending:
for cookie in evictions.keys():
if not db.has_key(cookie):
del evictions[cookie]
db['version'] = mm_cfg.PENDING_FILE_SCHEMA_VERSION
db['version'] = config.PENDING_FILE_SCHEMA_VERSION
tmpfile = '%s.tmp.%d.%d' % (self.__pendfile, os.getpid(), now)
omask = os.umask(007)
try:
......@@ -145,8 +148,10 @@ class Pending:
self.__save(db)
return content
def pend_repend(self, cookie, data, lifetime=mm_cfg.PENDING_REQUEST_LIFE):
def pend_repend(self, cookie, data, lifetime=_default):
assert self.Locked()
if lifetime is _default:
lifetime = config.PENDING_REQUEST_LIFE
db = self.__load()
db[cookie] = data
db['evictions'][cookie] = time.time() + lifetime
......@@ -173,9 +178,9 @@ def _update(olddb):
# subscription language. Best we can do here is use the server
# default.
db[cookie] = (SUBSCRIPTION,) + data[:-1] + \
(mm_cfg.DEFAULT_SERVER_LANGUAGE,)
(config.DEFAULT_SERVER_LANGUAGE,)
# The old database format kept the timestamp as the time the request
# was made. The new format keeps it as the time the request should be
# evicted.
evictions[cookie] = data[-1] + mm_cfg.PENDING_REQUEST_LIFE
evictions[cookie] = data[-1] + config.PENDING_REQUEST_LIFE
return db
......@@ -61,6 +61,8 @@ server = make_server(config.HTTP_HOST, config.HTTP_PORT,
qlog.info('HTTPRunner qrunner started.')
server.serve_forever()
# We'll never get here, but just in case...
qlog.info('HTTPRunner qrunner exiting.')
try:
server.serve_forever()
except:
qlog.exception('HTTPRunner qrunner exiting.')
raise
This diff is collapsed.
......@@ -28,7 +28,7 @@
#
# Each cookie has the following ingredients: the authorization context's
# secret (i.e. the password, and a timestamp. We generate an SHA1 hex
# digest of these ingredients, which we call the `mac'. We then marshal
# digest of these ingredients, which we call the 'mac'. We then marshal
# up a tuple of the timestamp and the mac, hexlify that and return that as
# a cookie keyed off the authcontext. Note that authenticating the user
# also requires the user's email address to be included in the cookie.
......@@ -48,7 +48,6 @@
import os
import re
import md5
import sha
import time
import urllib
......@@ -64,19 +63,15 @@ from Mailman import Errors
from Mailman import Utils
from Mailman.configuration import config
try:
import crypt
except ImportError:
crypt = None
log = logging.getLogger('mailman.error')
dlog = logging.getLogger('mailman.debug')
SLASH = '/'
class SecurityManager:
def InitVars(self):
# We used to set self.password here, from a crypted_password argument,
# but that's been removed when we generalized the mixin architecture.
# self.password is really a SecurityManager attribute, but it's set in
# MailList.InitVars().
self.mod_password = None
......@@ -144,50 +139,15 @@ class SecurityManager:
if ok:
return Defaults.AuthSiteAdmin
elif ac == Defaults.AuthListAdmin:
def cryptmatchp(response, secret):
try:
salt = secret[:2]
if crypt and crypt.crypt(response, salt) == secret:
return True
return False
except TypeError:
# BAW: Hard to say why we can get a TypeError here.
# SF bug report #585776 says crypt.crypt() can raise
# this if salt contains null bytes, although I don't
# know how that can happen (perhaps if a MM2.0 list
# with USE_CRYPT = 0 has been updated? Doubtful.
return False
# The password for the list admin and list moderator are not
# kept as plain text, but instead as an sha hexdigest. The
# response being passed in is plain text, so we need to
# digestify it first. Note however, that for backwards
# compatibility reasons, we'll also check the admin response